diff --git a/.github/workflows/peer-api.yaml b/.github/workflows/peer-api.yaml new file mode 100644 index 0000000000..6631d8562c --- /dev/null +++ b/.github/workflows/peer-api.yaml @@ -0,0 +1,22 @@ +name: Ensure API Peer Dependency + +on: + push: + branches: + - main + pull_request: + +jobs: + peer-api-check: + runs-on: ubuntu-latest + container: + image: node:14 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install lerna + run: npm install -g lerna + + - name: Check API dependency semantics + run: lerna exec "node ../../scripts/peer-api-check.js" diff --git a/CHANGELOG.md b/CHANGELOG.md index 63b26ad085..28ece4d320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,80 @@ All notable changes to this project will be documented in this file. ## Unreleased +## 0.18.2 + +### :bug: (Bug Fix) + +* `opentelemetry-api-metrics`, `opentelemetry-context-async-hooks`, `opentelemetry-context-zone-peer-dep`, `opentelemetry-core`, `opentelemetry-exporter-collector-grpc`, `opentelemetry-exporter-collector-proto`, `opentelemetry-exporter-collector`, `opentelemetry-exporter-jaeger`, `opentelemetry-exporter-prometheus`, `opentelemetry-exporter-zipkin`, `opentelemetry-grpc-utils`, `opentelemetry-instrumentation-fetch`, `opentelemetry-instrumentation-grpc`, `opentelemetry-instrumentation-http`, `opentelemetry-instrumentation-xml-http-request`, `opentelemetry-instrumentation`, `opentelemetry-metrics`, `opentelemetry-node`, `opentelemetry-plugin-grpc-js`, `opentelemetry-plugin-grpc`, `opentelemetry-plugin-http`, `opentelemetry-plugin-https`, `opentelemetry-propagator-b3`, `opentelemetry-propagator-jaeger`, `opentelemetry-resource-detector-aws`, `opentelemetry-resource-detector-gcp`, `opentelemetry-resources`, `opentelemetry-sdk-node`, `opentelemetry-shim-opentracing`, `opentelemetry-tracing`, `opentelemetry-web` + * [#2056](https://github.com/open-telemetry/opentelemetry-js/pull/2056) chore: downgrade API for patch release ([@dyladan](https://github.com/dyladan)) + +### Committers: 1 + +* Daniel Dyla ([@dyladan](https://github.com/dyladan)) + +## 0.18.1 + +### :rocket: (Enhancement) + +* `opentelemetry-instrumentation-fetch`, `opentelemetry-web` + * [#2010](https://github.com/open-telemetry/opentelemetry-js/pull/2010) Server side rendering support ([@ryhinchey](https://github.com/ryhinchey)) +* `opentelemetry-semantic-conventions` + * [#2026](https://github.com/open-telemetry/opentelemetry-js/pull/2026) feat: add NET_TRANSPORT IPC attributes ([@seemk](https://github.com/seemk)) +* `opentelemetry-instrumentation` + * [#1999](https://github.com/open-telemetry/opentelemetry-js/pull/1999) chore: fixing path of instrumentation file for different systems ([@obecny](https://github.com/obecny)) +* `opentelemetry-instrumentation-grpc` + * [#2005](https://github.com/open-telemetry/opentelemetry-js/pull/2005) chore: exporting grpc instrumentation config ([@obecny](https://github.com/obecny)) + +### :bug: (Bug Fix) + +* `opentelemetry-sdk-node` + * [#2006](https://github.com/open-telemetry/opentelemetry-js/pull/2006) chore: replacing console with diag ([@obecny](https://github.com/obecny)) + +### :books: (Refine Doc) + +* `opentelemetry-resource-detector-gcp` + * [#2002](https://github.com/open-telemetry/opentelemetry-js/pull/2002) doc: add usage to README.md of gcp detector ([@weyert](https://github.com/weyert)) +* `opentelemetry-api-metrics`, `opentelemetry-context-async-hooks`, `opentelemetry-context-zone-peer-dep`, `opentelemetry-context-zone`, `opentelemetry-core`, `opentelemetry-exporter-collector-grpc`, `opentelemetry-exporter-collector-proto`, `opentelemetry-exporter-collector`, `opentelemetry-exporter-jaeger`, `opentelemetry-exporter-prometheus`, `opentelemetry-exporter-zipkin`, `opentelemetry-grpc-utils`, `opentelemetry-instrumentation-fetch`, `opentelemetry-instrumentation-grpc`, `opentelemetry-instrumentation-http`, `opentelemetry-instrumentation-xml-http-request`, `opentelemetry-instrumentation`, `opentelemetry-metrics`, `opentelemetry-node`, `opentelemetry-plugin-grpc-js`, `opentelemetry-plugin-grpc`, `opentelemetry-plugin-http`, `opentelemetry-plugin-https`, `opentelemetry-propagator-b3`, `opentelemetry-resource-detector-aws`, `opentelemetry-resource-detector-gcp`, `opentelemetry-resources`, `opentelemetry-sdk-node`, `opentelemetry-semantic-conventions`, `opentelemetry-shim-opentracing`, `opentelemetry-tracing`, `opentelemetry-web` + * [#2040](https://github.com/open-telemetry/opentelemetry-js/pull/2040) chore: fixing broken links, updating to correct base url ([@obecny](https://github.com/obecny)) +* `opentelemetry-resources` + * [#2031](https://github.com/open-telemetry/opentelemetry-js/pull/2031) chore: add resource example ([@vknelluri](https://github.com/vknelluri)) +* Other + * [#2021](https://github.com/open-telemetry/opentelemetry-js/pull/2021) chore: updating compatibility matrix ([@obecny](https://github.com/obecny)) +* `opentelemetry-core` + * [#2011](https://github.com/open-telemetry/opentelemetry-js/pull/2011) docs: fix links & headings about built-in samplers ([@pokutuna](https://github.com/pokutuna)) + +### :house: (Internal) + +* Other + * [#2028](https://github.com/open-telemetry/opentelemetry-js/pull/2028) chore: removing examples of packages that are part of contrib repo ([@obecny](https://github.com/obecny)) + * [#2033](https://github.com/open-telemetry/opentelemetry-js/pull/2033) chore: add husky to renovate ignore ([@dyladan](https://github.com/dyladan)) + * [#1985](https://github.com/open-telemetry/opentelemetry-js/pull/1985) chore: fix renovate config ([@dyladan](https://github.com/dyladan)) + * [#1992](https://github.com/open-telemetry/opentelemetry-js/pull/1992) chore: update eslint ([@Flarna](https://github.com/Flarna)) + * [#1981](https://github.com/open-telemetry/opentelemetry-js/pull/1981) chore: do not pin the api package ([@dyladan](https://github.com/dyladan)) +* `opentelemetry-api-metrics`, `opentelemetry-context-async-hooks`, `opentelemetry-context-zone-peer-dep`, `opentelemetry-core`, `opentelemetry-exporter-collector-grpc`, `opentelemetry-exporter-collector-proto`, `opentelemetry-exporter-collector`, `opentelemetry-exporter-jaeger`, `opentelemetry-exporter-prometheus`, `opentelemetry-exporter-zipkin`, `opentelemetry-grpc-utils`, `opentelemetry-instrumentation-fetch`, `opentelemetry-instrumentation-grpc`, `opentelemetry-instrumentation-http`, `opentelemetry-instrumentation-xml-http-request`, `opentelemetry-instrumentation`, `opentelemetry-metrics`, `opentelemetry-node`, `opentelemetry-plugin-grpc-js`, `opentelemetry-plugin-grpc`, `opentelemetry-plugin-http`, `opentelemetry-plugin-https`, `opentelemetry-propagator-b3`, `opentelemetry-resource-detector-aws`, `opentelemetry-resource-detector-gcp`, `opentelemetry-resources`, `opentelemetry-sdk-node`, `opentelemetry-shim-opentracing`, `opentelemetry-tracing`, `opentelemetry-web` + * [#2038](https://github.com/open-telemetry/opentelemetry-js/pull/2038) chore: use api release candidate ([@dyladan](https://github.com/dyladan)) +* `opentelemetry-exporter-zipkin` + * [#2039](https://github.com/open-telemetry/opentelemetry-js/pull/2039) Check type of navigator.sendBeacon ([@dyladan](https://github.com/dyladan)) +* `opentelemetry-core`, `opentelemetry-exporter-collector`, `opentelemetry-instrumentation-fetch`, `opentelemetry-metrics`, `opentelemetry-propagator-b3` + * [#1978](https://github.com/open-telemetry/opentelemetry-js/pull/1978) chore: don't disable rule eqeqeq ([@Flarna](https://github.com/Flarna)) +* `opentelemetry-propagator-jaeger` + * [#1931](https://github.com/open-telemetry/opentelemetry-js/pull/1931) adopt opentelemetry-propagator-jaeger ([@jtmalinowski](https://github.com/jtmalinowski)) + +### Committers: 12 + +* Bartlomiej Obecny ([@obecny](https://github.com/obecny)) +* Daniel Dyla ([@dyladan](https://github.com/dyladan)) +* Gerhard Stöbich ([@Flarna](https://github.com/Flarna)) +* Jakub Malinowski ([@jtmalinowski](https://github.com/jtmalinowski)) +* Neil Fordyce ([@neilfordyce](https://github.com/neilfordyce)) +* Nir Hadassi ([@nirsky](https://github.com/nirsky)) +* Ryan Hinchey ([@ryhinchey](https://github.com/ryhinchey)) +* SJ ([@skjindal93](https://github.com/skjindal93)) +* Siim Kallas ([@seemk](https://github.com/seemk)) +* Weyert de Boer ([@weyert](https://github.com/weyert)) +* [@vknelluri](https://github.com/vknelluri) +* pokutuna ([@pokutuna](https://github.com/pokutuna)) + ## 0.18.0 ### :boom: Breaking Change diff --git a/README.md b/README.md index 32ccfda003..08ef023783 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ Maintainers ([@open-telemetry/js-maintainers](https://github.com/orgs/open-telem | Package | Description | |----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [@opentelemetry/tracing][otel-tracing] | This module provides a full control over instrumentation and span creation. It doesn't load [`async_hooks`](https://nodejs.org/api/async_hooks.html) or any instrumentation plugin by default. It is intended for use both on the server and in the browser. | +| [@opentelemetry/tracing][otel-tracing] | This module provides a full control over instrumentation and span creation. It doesn't load [`async_hooks`](https://nodejs.org/api/async_hooks.html) or any instrumentation by default. It is intended for use both on the server and in the browser. | | [@opentelemetry/metrics][otel-metrics] | This module provides instruments and meters for reporting of time series data. | | [@opentelemetry/node][otel-node] | This module provides automatic tracing for Node.js applications. It is intended for use on the server only. | | [@opentelemetry/web][otel-web] | This module provides automated instrumentation and tracing for Web applications. It is intended for use in the browser only. | @@ -196,34 +196,32 @@ OpenTelemetry is vendor-agnostic and can upload data to any backend with various See the [OpenTelemetry registry](https://opentelemetry.io/registry/?s=node.js) for a list of exporters available. -### Instrumentations & Plugins +### Instrumentations OpenTelemetry can collect tracing data automatically using instrumentations. Vendors/Users can also create and use their own. Currently, OpenTelemetry supports automatic tracing for: -#### Node Instrumentations & Plugins +#### Node Instrumentations ##### Core -- [@opentelemetry/instrumentation-grpc][otel-instrumentation-grpc] previous [@opentelemetry/plugin-grpc][otel-plugin-grpc] -- [@opentelemetry/plugin-grpc-js][otel-plugin-grpc-js] -- [@opentelemetry/instrumentation-http][otel-instrumentation-http] previous [@opentelemetry/plugin-http][otel-plugin-http] and [@opentelemetry/plugin-https][otel-plugin-https] +- [@opentelemetry/instrumentation-grpc][otel-instrumentation-grpc] +- [@opentelemetry/instrumentation-http][otel-instrumentation-http] ##### Contrib These plugins are hosted at -- [@opentelemetry/plugin-mongodb][otel-contrib-plugin-mongodb] -- [@opentelemetry/plugin-mysql][otel-contrib-plugin-mysql] -- [@opentelemetry/plugin-pg][otel-contrib-plugin-pg] -- [@opentelemetry/plugin-pg-pool][otel-contrib-plugin-pg-pool] -- [@opentelemetry/plugin-redis][otel-contrib-plugin-redis] -- [@opentelemetry/plugin-ioredis][otel-contrib-plugin-ioredis] -- [@opentelemetry/plugin-express][otel-contrib-plugin-express] -- [@opentelemetry/plugin-dns][otel-contrib-plugin-dns] +- [@opentelemetry/instrumentation-mongodb][otel-contrib-instrumentation-mongodb] +- [@opentelemetry/instrumentation-mysql][otel-contrib-instrumentation-mysql] +- [@opentelemetry/instrumentation-pg][otel-contrib-instrumentation-pg] +- [@opentelemetry/instrumentation-redis][otel-contrib-instrumentation-redis] +- [@opentelemetry/instrumentation-ioredis][otel-contrib-instrumentation-ioredis] +- [@opentelemetry/instrumentation-express][otel-contrib-instrumentation-express] +- [@opentelemetry/instrumentation-dns][otel-contrib-instrumentation-dns] - [@opentelemetry/instrumentation-hapi][otel-contrib-instrumentation-hapi] - [@opentelemetry/instrumentation-koa][otel-contrib-instrumentation-koa] -#### Web Plugins +#### Web Instrumentations ##### Core @@ -232,7 +230,7 @@ These plugins are hosted at +These instrumentations are hosted at - [@opentelemetry/instrumentation-document-load][otel-contrib-instrumentation-document-load] - [@opentelemetry/instrumentation-user-interaction][otel-contrib-instrumentation-user-interaction] @@ -247,6 +245,12 @@ To request automatic tracing support for a module not on this list, please [file ## Upgrade guidelines +### 0.18.x to 0.19.0 + +- All plugins have been removed in favor of instrumentations. + +- The `@opentelemetry/propagator-b3` package previously exported three propagators: `B3Propagator`,`B3SinglePropagator`, and `B3MultiPropagator`, but now only exports the `B3Propagator`. It extracts b3 context in single and multi-header encodings, and injects context using the single-header encoding by default, but can be configured to inject context using the multi-header endcoding during construction: `new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER })`. If you were previously using the `B3SinglePropagator` or `B3MultiPropagator` directly, you should update your code to use the `B3Propagator` with the appropriate configuration. See the [readme](./packages/opentelemetry-propagator-b3/readme.md) for full details and usage. + ### 0.17.0 to 0.18.0 - `diag.setLogLevel` is removed and LogLevel can be set by an optional second parameter to `setLogger` @@ -411,11 +415,6 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [otel-metrics]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-metrics [otel-node]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node -[otel-plugin-grpc]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc -[otel-plugin-grpc-js]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc-js -[otel-plugin-http]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-http -[otel-plugin-https]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-https - [otel-instrumentation-fetch]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-fetch [otel-instrumentation-grpc]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-grpc [otel-instrumentation-http]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-http @@ -428,14 +427,13 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [otel-core]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core [generate-api-documentation]: https://github.com/open-telemetry/opentelemetry-js/blob/main/CONTRIBUTING.md#generating-api-documentation -[otel-contrib-plugin-dns]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-dns -[otel-contrib-plugin-ioredis]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-ioredis -[otel-contrib-plugin-mongodb]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-mongodb -[otel-contrib-plugin-mysql]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-mysql -[otel-contrib-plugin-pg-pool]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-pg-pool -[otel-contrib-plugin-pg]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-pg -[otel-contrib-plugin-redis]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-redis -[otel-contrib-plugin-express]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-plugin-express +[otel-contrib-instrumentation-dns]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-dns +[otel-contrib-instrumentation-ioredis]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-ioredis +[otel-contrib-instrumentation-mongodb]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-mongodb +[otel-contrib-instrumentation-mysql]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-mysql +[otel-contrib-instrumentation-pg]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-pg +[otel-contrib-instrumentation-redis]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-redis +[otel-contrib-instrumentation-express]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-express [otel-contrib-instrumentation-user-interaction]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/web/opentelemetry-instrumentation-user-interaction [otel-contrib-instrumentation-document-load]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/web/opentelemetry-instrumentation-document-load [otel-contrib-instrumentation-hapi]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/master/plugins/node/opentelemetry-instrumentation-hapi diff --git a/backwards-compatability/node10/package.json b/backwards-compatability/node10/package.json index 0cd01f7597..00cf230b7c 100644 --- a/backwards-compatability/node10/package.json +++ b/backwards-compatability/node10/package.json @@ -1,6 +1,6 @@ { "name": "backcompat-node10", - "version": "0.18.0", + "version": "0.18.2", "private": true, "description": "Backwards compatability app for node8 types and the OpenTelemetry Node.js SDK", "main": "index.js", @@ -8,12 +8,12 @@ "test:backcompat": "tsc --noEmit index.ts" }, "dependencies": { - "@opentelemetry/sdk-node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/sdk-node": "0.18.2", + "@opentelemetry/tracing": "0.18.2" }, "devDependencies": { - "@types/node": "10.17.55", - "typescript": "4.2.3" + "@types/node": "10.17.56", + "typescript": "4.2.4" }, "author": "OpenTelemetry Authors", "license": "Apache-2.0" diff --git a/backwards-compatability/node12/package.json b/backwards-compatability/node12/package.json index 265a118491..16994dc8a8 100644 --- a/backwards-compatability/node12/package.json +++ b/backwards-compatability/node12/package.json @@ -1,6 +1,6 @@ { "name": "backcompat-node12", - "version": "0.18.0", + "version": "0.18.2", "private": true, "description": "Backwards compatability app for node8 types and the OpenTelemetry Node.js SDK", "main": "index.js", @@ -8,12 +8,12 @@ "test:backcompat": "tsc --noEmit index.ts" }, "dependencies": { - "@opentelemetry/sdk-node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/sdk-node": "0.18.2", + "@opentelemetry/tracing": "0.18.2" }, "devDependencies": { - "@types/node": "12.20.6", - "typescript": "4.2.3" + "@types/node": "12.20.7", + "typescript": "4.2.4" }, "author": "OpenTelemetry Authors", "license": "Apache-2.0" diff --git a/backwards-compatability/node8/package.json b/backwards-compatability/node8/package.json index f85d56fec7..f4b8aa6b45 100644 --- a/backwards-compatability/node8/package.json +++ b/backwards-compatability/node8/package.json @@ -1,6 +1,6 @@ { "name": "backcompat-node8", - "version": "0.18.0", + "version": "0.18.2", "private": true, "description": "Backwards compatability app for node8 types and the OpenTelemetry Node.js SDK", "main": "index.js", @@ -8,12 +8,12 @@ "test:backcompat": "tsc --noEmit index.ts" }, "dependencies": { - "@opentelemetry/sdk-node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/sdk-node": "0.18.2", + "@opentelemetry/tracing": "0.18.2" }, "devDependencies": { "@types/node": "8.10.66", - "typescript": "4.2.3" + "typescript": "4.2.4" }, "author": "OpenTelemetry Authors", "license": "Apache-2.0" diff --git a/doc/instrumentation-guide.md b/doc/instrumentation-guide.md index fd566ed119..84a10a0425 100644 --- a/doc/instrumentation-guide.md +++ b/doc/instrumentation-guide.md @@ -1,52 +1,11 @@ # Instrumentation Developer Guide -We provide out-of-the-box instrumentations for many popular frameworks and libraries by using an instrumentation system (see [builtin instrumentations][builtin-instrumentations]), and provide a means for developers to create their own. +A detailed explained guide how to instrument a package is available at [instrumentation package][base-instrumentation] -We strongly recommended to create a dedicated package for newly added plugin, example: `@opentelemetry/plugin-xxx`. +For more comprehensive examples please refer to the [HTTP instrumentation][http-instrumentation] or [gRPC instrumentation][grpc-instrumentation] for node +and [XMLHttpRequest instrumentation][xhr-instrumentation] for web. -Each plugin must extend the abstract class [BasePlugin][base-plugin] implementing the below methods: - -- `patch`: A function describing how the module exports for a given file should be modified. - -- `unpatch`: A function describing how the module exports for a given file should be unpatched. This should generally mirror the logic in `patch`; for example, if `patch` wraps a method, `unpatch` should unwrap it. - -The core `PluginLoader` class is responsible for loading the instrumented plugins that use a patch mechanism to enable automatic tracing for specific target modules. In order to load new plugin, it should export `plugin` identifier. - -```typescript -export const plugin = new HttpPlugin(...); -``` - -> Example of simple module plugin created and used in the tests. - - -After the plugin is created, it must be added in the [list of default supported plugins][DEFAULT_INSTRUMENTATION_PLUGINS]. - -```typescript -export const DEFAULT_INSTRUMENTATION_PLUGINS: Plugins = { - http: { - enabled: true, - path: '@opentelemetry/plugin-http', - }, - grpc: { - enabled: true, - path: '@opentelemetry/plugin-grpc', - }, - // [ADD NEW PLUGIN HERE] - xxx: { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-xxx', - } -}; -``` - -We recommend using [`shimmer`][shimmer] to modify function properties on objects. - -Please refer to the [HTTP instrumentation][http-plugin] or [gRPC instrumentation][grpc-plugin] for more comprehensive examples. - -[shimmer]: https://github.com/othiym23/shimmer -[builtin-instrumentations]: https://github.com/open-telemetry/opentelemetry-js#instrumentations&plugins -[base-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/src/platform/node/BasePlugin.ts -[http-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/http.ts#L44 -[grpc-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-grpc/src/grpc.ts#L52 -[DEFAULT_INSTRUMENTATION_PLUGINS]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-node/src/config.ts#L29 +[base-instrumentation]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation +[http-instrumentation]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-instrumentation-http/src/http.ts#L59 +[grpc-instrumentation]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-instrumentation-grpc/src/instrumentation.ts#L28 +[xhr-instrumentation]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts#L71 diff --git a/examples/basic-tracer-node/package.json b/examples/basic-tracer-node/package.json index ea261a4161..fe28bd9162 100644 --- a/examples/basic-tracer-node/package.json +++ b/examples/basic-tracer-node/package.json @@ -1,7 +1,7 @@ { "name": "example-basic-tracer-node", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/tracing in Node.js", "main": "index.js", "scripts": { @@ -25,8 +25,8 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme" } diff --git a/examples/collector-exporter-node/package.json b/examples/collector-exporter-node/package.json index 407f135d1d..0b92ef33b5 100644 --- a/examples/collector-exporter-node/package.json +++ b/examples/collector-exporter-node/package.json @@ -1,7 +1,7 @@ { "name": "example-collector-exporter-node", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/collector-exporter in Node.js", "main": "index.js", "scripts": { @@ -29,12 +29,12 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-collector": "^0.18.0", - "@opentelemetry/exporter-collector-grpc": "^0.18.0", - "@opentelemetry/exporter-collector-proto": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/core": "^0.18.2", + "@opentelemetry/exporter-collector": "^0.18.2", + "@opentelemetry/exporter-collector-grpc": "^0.18.2", + "@opentelemetry/exporter-collector-proto": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme" } diff --git a/examples/grpc-js/package.json b/examples/grpc-js/package.json index a73eb0ed80..e2c72adde1 100644 --- a/examples/grpc-js/package.json +++ b/examples/grpc-js/package.json @@ -1,7 +1,7 @@ { "name": "grpc-js-example", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of @grpc/grpc-js integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -28,14 +28,14 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@grpc/grpc-js": "^1.0.5", + "@grpc/grpc-js": "^1.2.12", "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/plugin-grpc-js": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-grpc": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", "google-protobuf": "^3.9.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", diff --git a/examples/grpc-js/tracer.js b/examples/grpc-js/tracer.js index 84e0012aef..21f371dcd7 100644 --- a/examples/grpc-js/tracer.js +++ b/examples/grpc-js/tracer.js @@ -6,6 +6,7 @@ const { NodeTracerProvider } = require('@opentelemetry/node'); const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const { GrpcInstrumentation } = require('@opentelemetry/instrumentation-grpc'); const EXPORTER = process.env.EXPORTER || ''; @@ -13,21 +14,7 @@ module.exports = (serviceName) => { const provider = new NodeTracerProvider(); registerInstrumentations({ instrumentations: [ - { - plugins: { - '@grpc/grpc-js': { - enabled: true, - path: '@opentelemetry/plugin-grpc-js', - // // when boostraping with lerna for testing purposes - // path: `${__dirname}/../../packages/opentelemetry-plugin-grpc-js/build/src` - }, - // // when boostraping with lerna for testing purposes - // 'http': { - // enabled: true, - // path: `${__dirname}/../../packages/opentelemetry-plugin-http/build/src` - // }, - }, - }, + new GrpcInstrumentation(), ], tracerProvider: provider, }); diff --git a/examples/grpc/package.json b/examples/grpc/package.json index 53ab194b5d..5320e9c49e 100644 --- a/examples/grpc/package.json +++ b/examples/grpc/package.json @@ -1,7 +1,7 @@ { "name": "grpc-example", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of gRPC integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -29,12 +29,12 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/plugin-grpc": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-grpc": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", "google-protobuf": "^3.9.2", "grpc": "^1.23.3", "node-pre-gyp": "0.14.0" diff --git a/examples/grpc/tracer.js b/examples/grpc/tracer.js index 7dd9aaaaca..d880cb873b 100644 --- a/examples/grpc/tracer.js +++ b/examples/grpc/tracer.js @@ -6,6 +6,7 @@ const { NodeTracerProvider } = require('@opentelemetry/node'); const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const { GrpcInstrumentation } = require('@opentelemetry/instrumentation-grpc'); const EXPORTER = process.env.EXPORTER || ''; @@ -13,17 +14,9 @@ module.exports = (serviceName) => { const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, - // // when boostraping with lerna for testing purposes - // instrumentations: [ - // { - // plugins: { - // grpc: { - // enabled: true, - // path: `${__dirname}/../../packages/opentelemetry-plugin-grpc/build/src` - // } - // } - // } - // ], + instrumentations: [ + new GrpcInstrumentation(), + ], }); let exporter; diff --git a/examples/http/package.json b/examples/http/package.json index 99b3ef2b60..96b198dbbc 100644 --- a/examples/http/package.json +++ b/examples/http/package.json @@ -1,7 +1,7 @@ { "name": "http-example", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of HTTP integration with OpenTelemetry", "main": "index.js", "scripts": { @@ -29,12 +29,12 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/plugin-http": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-http": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", "devDependencies": { diff --git a/examples/http/tracer.js b/examples/http/tracer.js index 1e3d5244e0..db7e219132 100644 --- a/examples/http/tracer.js +++ b/examples/http/tracer.js @@ -6,6 +6,7 @@ const { NodeTracerProvider } = require('@opentelemetry/node'); const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const EXPORTER = process.env.EXPORTER || ''; @@ -14,16 +15,9 @@ module.exports = (serviceName) => { registerInstrumentations({ tracerProvider: provider, // // when boostraping with lerna for testing purposes - // instrumentations: [ - // { - // plugins: { - // http: { - // enabled: true, - // path: `${__dirname}/../../packages/opentelemetry-plugin-http/build/src` - // } - // } - // } - // ], + instrumentations: [ + new HttpInstrumentation(), + ], }); let exporter; diff --git a/examples/https/package.json b/examples/https/package.json index ef86b5da32..b0b66a73d4 100644 --- a/examples/https/package.json +++ b/examples/https/package.json @@ -1,7 +1,7 @@ { "name": "https-example", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of HTTPs integration with OpenTelemetry", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -30,12 +30,12 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/plugin-https": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-http": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", "devDependencies": { diff --git a/examples/https/tracer.js b/examples/https/tracer.js index a91557155f..c239467ea0 100644 --- a/examples/https/tracer.js +++ b/examples/https/tracer.js @@ -6,6 +6,7 @@ const { NodeTracerProvider } = require('@opentelemetry/node'); const { SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const EXPORTER = process.env.EXPORTER || ''; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; @@ -15,17 +16,9 @@ module.exports = (serviceName) => { const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, - // when boostraping with lerna for testing purposes - // instrumentations: [ - // { - // plugins: { - // https: { - // enabled: true, - // path: `${__dirname}/../../packages/opentelemetry-plugin-https/build/src` - // } - // } - // } - // ], + instrumentations: [ + new HttpInstrumentation(), + ], }); if (EXPORTER.toLowerCase().startsWith('z')) { diff --git a/examples/metrics/package.json b/examples/metrics/package.json index 7ab8e48979..3453903482 100644 --- a/examples/metrics/package.json +++ b/examples/metrics/package.json @@ -1,7 +1,7 @@ { "name": "example-metrics", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/metrics", "main": "index.js", "scripts": { @@ -26,9 +26,9 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-prometheus": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0" + "@opentelemetry/core": "^0.18.2", + "@opentelemetry/exporter-prometheus": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme" } diff --git a/examples/opentracing-shim/package.json b/examples/opentracing-shim/package.json index 0ca735f450..7f8411e4ea 100644 --- a/examples/opentracing-shim/package.json +++ b/examples/opentracing-shim/package.json @@ -1,7 +1,7 @@ { "name": "opentracing-shim", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/shim-opentracing in Node.js", "main": "index.js", "scripts": { @@ -29,12 +29,12 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@opentelemetry/exporter-jaeger": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/shim-opentracing": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/exporter-jaeger": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/shim-opentracing": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", "opentracing": "^0.14.4" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", diff --git a/examples/opentracing-shim/shim.js b/examples/opentracing-shim/shim.js index 40ad299e28..e5d0209320 100644 --- a/examples/opentracing-shim/shim.js +++ b/examples/opentracing-shim/shim.js @@ -11,17 +11,6 @@ function shim(serviceName) { const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, - // // when boostraping with lerna for testing purposes - // instrumentations: [ - // { - // plugins: { - // 'opentracing': { - // enabled: true, - // path: `${__dirname}/../../packages/opentelemetry-shim-opentracing/build/src` - // } - // } - // } - // ], }); provider.addSpanProcessor(new SimpleSpanProcessor(getExporter(serviceName))); diff --git a/examples/prometheus/package.json b/examples/prometheus/package.json index 229bc78d6f..64eef72681 100644 --- a/examples/prometheus/package.json +++ b/examples/prometheus/package.json @@ -1,6 +1,6 @@ { "name": "prometheus-example", - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/metrics and @opentelemetry/exporter-prometheus", "main": "index.js", "scripts": { @@ -9,7 +9,7 @@ "author": "OpenTelemetry Authors", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/exporter-prometheus": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0" + "@opentelemetry/exporter-prometheus": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2" } } diff --git a/examples/tracer-web/package.json b/examples/tracer-web/package.json index 5c428587ae..2086ba5025 100644 --- a/examples/tracer-web/package.json +++ b/examples/tracer-web/package.json @@ -1,7 +1,7 @@ { "name": "web-tracer-example", "private": true, - "version": "0.18.0", + "version": "0.18.2", "description": "Example of using @opentelemetry/web in browser", "main": "index.js", "scripts": { @@ -35,17 +35,17 @@ }, "dependencies": { "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/context-zone": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-collector": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/instrumentation-fetch": "^0.18.0", - "@opentelemetry/instrumentation-xml-http-request": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/propagator-b3": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@opentelemetry/web": "^0.18.0" + "@opentelemetry/context-zone": "^0.18.2", + "@opentelemetry/core": "^0.18.2", + "@opentelemetry/exporter-collector": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-fetch": "^0.18.2", + "@opentelemetry/instrumentation-xml-http-request": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2", + "@opentelemetry/propagator-b3": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", + "@opentelemetry/web": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme" } diff --git a/getting-started/README.md b/getting-started/README.md index d97ed45270..cdb31d53a3 100644 --- a/getting-started/README.md +++ b/getting-started/README.md @@ -54,15 +54,14 @@ This guide uses the example application provided in the [example directory](exam ([link to TypeScript version](ts-example/README.md#install-the-required-opentelemetry-libraries)) -To create traces on NodeJS, you need `@opentelemetry/node`, `@opentelemetry/core`, and any plugins required by your application such as gRPC or HTTP. If you're using the example application, you need to install `@opentelemetry/plugin-http`, `@opentelemetry/plugin-https`, and `@opentelemetry/plugin-express`. +To create traces on NodeJS, you need `@opentelemetry/node`, `@opentelemetry/core`, and any instrumentation required by your application such as gRPC or HTTP. If you're using the example application, you need to install `@opentelemetry/instrumentation-http` and `@opentelemetry/instrumentation-express`. ```sh $ npm install \ @opentelemetry/core \ @opentelemetry/node \ - @opentelemetry/plugin-http \ - @opentelemetry/plugin-https \ - @opentelemetry/plugin-express + @opentelemetry/instrumentation-http \ + @opentelemetry/instrumentation-express ``` #### Initialize a global tracer @@ -76,17 +75,23 @@ Create a file named `tracing.js` and add the following code: ```javascript 'use strict'; -const { LogLevel } = require("@opentelemetry/core"); +const { diag, DiagConsoleLogger, DiagLogLevel } = require("@opentelemetry/api"); const { NodeTracerProvider } = require("@opentelemetry/node"); const { registerInstrumentations } = require("@opentelemetry/instrumentation"); +const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http"); +const { GrpcInstrumentation } = require("@opentelemetry/instrumentation-grpc"); -const provider = new NodeTracerProvider({ - logLevel: LogLevel.ERROR -}); +const provider = new NodeTracerProvider(); + +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL); provider.register(); registerInstrumentations({ + instrumentations: [ + new HttpInstrumentation(), + new GrpcInstrumentation(), + ], tracerProvider: provider, }); @@ -118,21 +123,17 @@ After you install these dependencies, initialize and register them. Modify `trac ```javascript 'use strict'; -const { LogLevel } = require("@opentelemetry/core"); +const { diag, DiagConsoleLogger, DiagLogLevel } = require("@opentelemetry/api"); const { NodeTracerProvider } = require("@opentelemetry/node"); const { SimpleSpanProcessor } = require("@opentelemetry/tracing"); const { ZipkinExporter } = require("@opentelemetry/exporter-zipkin"); const { registerInstrumentations } = require("@opentelemetry/instrumentation"); +const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http"); +const { GrpcInstrumentation } = require("@opentelemetry/instrumentation-grpc"); -const provider = new NodeTracerProvider({ - logLevel: LogLevel.ERROR -}); - -registerInstrumentations({ - tracerProvider: provider, -}); +const provider = new NodeTracerProvider(); -provider.register(); +diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ALL); provider.addSpanProcessor( new SimpleSpanProcessor( @@ -145,6 +146,16 @@ provider.addSpanProcessor( ) ); +provider.register(); + +registerInstrumentations({ + instrumentations: [ + new HttpInstrumentation(), + new GrpcInstrumentation(), + ], + tracerProvider: provider, +}); + console.log("tracing initialized"); ``` diff --git a/getting-started/example/package.json b/getting-started/example/package.json index a39dcb4095..55974e92a8 100644 --- a/getting-started/example/package.json +++ b/getting-started/example/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/getting-started-example", - "version": "0.18.0", + "version": "0.18.2", "description": "This repository provides everything required to follow the OpenTelemetry Getting Started Guide", "main": "app.js", "scripts": { diff --git a/getting-started/monitored-example/package.json b/getting-started/monitored-example/package.json index 81bbf523d9..7556f31ff2 100644 --- a/getting-started/monitored-example/package.json +++ b/getting-started/monitored-example/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/getting-started-monitored-example", - "version": "0.18.0", + "version": "0.18.2", "description": "This repository provides everything required to follow the OpenTelemetry Getting Started Guide", "main": "app.js", "scripts": { @@ -9,8 +9,8 @@ "author": "OpenTelemetry Authors", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/exporter-prometheus": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", + "@opentelemetry/exporter-prometheus": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2", "axios": "^0.21.0", "express": "^4.17.1" } diff --git a/getting-started/traced-example/package.json b/getting-started/traced-example/package.json index cfb5bcd21d..9470c00075 100644 --- a/getting-started/traced-example/package.json +++ b/getting-started/traced-example/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/getting-started-traced-example", - "version": "0.18.0", + "version": "0.18.2", "description": "This repository provides everything required to follow the OpenTelemetry Getting Started Guide", "main": "app.js", "scripts": { @@ -9,14 +9,13 @@ "author": "OpenTelemetry Authors", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/plugin-express": "^0.14.0", - "@opentelemetry/plugin-http": "^0.18.0", - "@opentelemetry/plugin-https": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/core": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/instrumentation-express": "^0.15.0", + "@opentelemetry/instrumentation-http": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", "axios": "^0.21.0", "express": "^4.17.1" } diff --git a/getting-started/traced-example/tracing.js b/getting-started/traced-example/tracing.js index 55b0fd62ec..bfd6ae60c4 100644 --- a/getting-started/traced-example/tracing.js +++ b/getting-started/traced-example/tracing.js @@ -4,6 +4,8 @@ const { NodeTracerProvider } = require("@opentelemetry/node"); const { SimpleSpanProcessor } = require("@opentelemetry/tracing"); const { ZipkinExporter } = require("@opentelemetry/exporter-zipkin"); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const provider = new NodeTracerProvider(); @@ -23,6 +25,10 @@ provider.register(); // load old default plugins registerInstrumentations({ tracerProvider: provider, + instrumentations: [ + new ExpressInstrumentation(), + new HttpInstrumentation(), + ], }); console.log("tracing initialized"); diff --git a/getting-started/ts-example/README.md b/getting-started/ts-example/README.md index b05bbd9e41..cfb8394ca5 100644 --- a/getting-started/ts-example/README.md +++ b/getting-started/ts-example/README.md @@ -60,9 +60,9 @@ To create traces on NodeJS, you will need `@opentelemetry/node`, `@opentelemetry $ npm install \ @opentelemetry/core \ @opentelemetry/node \ - @opentelemetry/plugin-http \ - @opentelemetry/plugin-https \ - @opentelemetry/plugin-express + @opentelemetry/instrumentation \ + @opentelemetry/instrumentation-http \ + @opentelemetry/instrumentation-express ``` #### Initialize a global tracer @@ -76,12 +76,25 @@ Create a file named `tracing.ts` and add the following code: ```typescript import { LogLevel } from '@opentelemetry/core'; import { NodeTracerProvider } from '@opentelemetry/node'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; + const provider: NodeTracerProvider = new NodeTracerProvider({ logLevel: LogLevel.ERROR, }); provider.register(); + +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + new ExpressInstrumentation(), + new HttpInstrumentation(), + ], +}); + ``` If you run your application now with `ts-node -r ./tracing.ts app.ts`, your application will create and propagate traces over HTTP. If an already instrumented service that supports [Trace Context](https://www.w3.org/TR/trace-context/) headers calls your application using HTTP, and you call another application using HTTP, the Trace Context headers will be correctly propagated. @@ -116,6 +129,10 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; // For Jaeger, use the following line instead: // import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; + const provider: NodeTracerProvider = new NodeTracerProvider({ logLevel: LogLevel.ERROR, }); @@ -135,6 +152,15 @@ provider.addSpanProcessor( ), ); +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + new ExpressInstrumentation(), + new HttpInstrumentation(), + ], +}); + + console.log('tracing initialized'); ``` diff --git a/getting-started/ts-example/example/package.json b/getting-started/ts-example/example/package.json index 3a6943f0be..5d24e4081a 100644 --- a/getting-started/ts-example/example/package.json +++ b/getting-started/ts-example/example/package.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "devDependencies": { "@types/express": "4.17.11", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "ts-node": "8.10.2" }, "dependencies": { diff --git a/getting-started/ts-example/monitored-example/package.json b/getting-started/ts-example/monitored-example/package.json index 48488a39f5..519fe66f05 100644 --- a/getting-started/ts-example/monitored-example/package.json +++ b/getting-started/ts-example/monitored-example/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/getting-started-monitored-ts-example", - "version": "0.18.0", + "version": "0.18.2", "description": "This repository provides everything required to follow the OpenTelemetry Getting Started Guide", "main": "app.ts", "scripts": { @@ -10,12 +10,12 @@ "license": "Apache-2.0", "devDependencies": { "@types/express": "4.17.11", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "ts-node": "8.10.2" }, "dependencies": { - "@opentelemetry/exporter-prometheus": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", + "@opentelemetry/exporter-prometheus": "^0.18.2", + "@opentelemetry/metrics": "^0.18.2", "axios": "^0.21.0", "express": "^4.17.1" } diff --git a/getting-started/ts-example/traced-example/package.json b/getting-started/ts-example/traced-example/package.json index 38043ffa7b..417b4c66c3 100644 --- a/getting-started/ts-example/traced-example/package.json +++ b/getting-started/ts-example/traced-example/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/getting-started-traced-ts-example", - "version": "0.18.0", + "version": "0.18.2", "description": "This repository provides everything required to follow the OpenTelemetry Getting Started Guide", "main": "app.ts", "scripts": { @@ -10,17 +10,17 @@ "license": "Apache-2.0", "devDependencies": { "@types/express": "4.17.11", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "ts-node": "9.1.1" }, "dependencies": { - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-zipkin": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/plugin-express": "^0.14.0", - "@opentelemetry/plugin-http": "^0.18.0", - "@opentelemetry/plugin-https": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/core": "^0.18.2", + "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", + "@opentelemetry/node": "^0.18.2", + "@opentelemetry/instrumentation-express": "^0.15.0", + "@opentelemetry/instrumentation-http": "^0.18.2", + "@opentelemetry/tracing": "^0.18.2", "axios": "^0.21.0", "express": "^4.17.1" } diff --git a/getting-started/ts-example/traced-example/tracing.ts b/getting-started/ts-example/traced-example/tracing.ts index aeb626ae57..ec7738f752 100644 --- a/getting-started/ts-example/traced-example/tracing.ts +++ b/getting-started/ts-example/traced-example/tracing.ts @@ -5,6 +5,10 @@ import { ZipkinExporter } from '@opentelemetry/exporter-zipkin'; // For Jaeger, use the following line instead: // import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); + const provider: NodeTracerProvider = new NodeTracerProvider(); provider.register(); @@ -22,4 +26,13 @@ provider.addSpanProcessor( ), ); +registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + new ExpressInstrumentation(), + new HttpInstrumentation(), + ], +}); + + console.log('tracing initialized'); diff --git a/integration-tests/propagation-validation-server/package.json b/integration-tests/propagation-validation-server/package.json index c88c85689c..0c425e41ab 100644 --- a/integration-tests/propagation-validation-server/package.json +++ b/integration-tests/propagation-validation-server/package.json @@ -1,6 +1,6 @@ { "name": "propagation-validation-server", - "version": "0.18.0", + "version": "0.18.2", "description": "server for w3c tests", "main": "validation_server.js", "private": true, @@ -11,15 +11,18 @@ "compile": "tsc --build" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/context-async-hooks": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "axios": "0.21.1", "body-parser": "1.19.0", "express": "4.17.1" }, "devDependencies": { - "typescript": "4.2.3" + "@opentelemetry/api": "^1.0.0-rc.0", + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" } } diff --git a/lerna.json b/lerna.json index cc63ae9253..a6a87773c7 100644 --- a/lerna.json +++ b/lerna.json @@ -8,7 +8,7 @@ "packages/*", "integration-tests/*" ], - "version": "0.18.0", + "version": "0.18.2", "changelog": { "repo": "open-telemetry/opentelemetry-js", "labels": { diff --git a/metapackages/plugins-node-core/README.md b/metapackages/plugins-node-core/README.md deleted file mode 100644 index eb5e1bea13..0000000000 --- a/metapackages/plugins-node-core/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# OpenTelemetry Plugins Node Core - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![Apache License][license-image]][license-image] - -This package depends on all core node plugins maintained by OpenTelemetry authors. Installing it installs all core plugins - -## Plugins - -- [@opentelemetry/plugin-grpc][otel-plugin-grpc] -- [@opentelemetry/plugin-grpc-js][otel-plugin-grpc-js] -- [@opentelemetry/plugin-http][otel-plugin-http] -- [@opentelemetry/plugin-https][otel-plugin-https] - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=metapackages/plugins-node-core -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetryplugins-node-core -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugins-node-core -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugins-node-core.svg - -[otel-plugin-grpc]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc -[otel-plugin-grpc-js]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc-js -[otel-plugin-http]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-http -[otel-plugin-https]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-https diff --git a/metapackages/plugins-node-core/package.json b/metapackages/plugins-node-core/package.json deleted file mode 100644 index 2c82b015ec..0000000000 --- a/metapackages/plugins-node-core/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@opentelemetry/plugins-node-core", - "version": "0.18.0", - "description": "Metapackage which bundles all opentelemetry node core plugins", - "author": "OpenTelemetry Authors", - "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/open-telemetry/opentelemetry-js.git" - }, - "bugs": { - "url": "https://github.com/open-telemetry/opentelemetry-js/issues" - }, - "dependencies": { - "@opentelemetry/plugin-grpc": "^0.18.0", - "@opentelemetry/plugin-grpc-js": "^0.18.0", - "@opentelemetry/plugin-http": "^0.18.0", - "@opentelemetry/plugin-https": "^0.18.0" - } -} diff --git a/metapackages/plugins-web-core/README.md b/metapackages/plugins-web-core/README.md deleted file mode 100644 index 25768a1dda..0000000000 --- a/metapackages/plugins-web-core/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# OpenTelemetry Plugins Web Core - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![Apache License][license-image]][license-image] - -This package depends on all core web plugins maintained by OpenTelemetry authors. Installing it installs all core plugins - -## Plugins - -- [@opentelemetry/instrumentation-xml-http-request][otel-instrumentation-xml-http-request] - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=metapackages/plugins-web-core -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetryplugins-web-core -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugins-web-core -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugins-web-core.svg - -[otel-instrumentation-xml-http-request]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-xml-http-request diff --git a/metapackages/plugins-web-core/package.json b/metapackages/plugins-web-core/package.json deleted file mode 100644 index 2b073cf0c8..0000000000 --- a/metapackages/plugins-web-core/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@opentelemetry/plugins-web-core", - "version": "0.18.0", - "description": "Metapackage which bundles all opentelemetry web core plugins", - "author": "OpenTelemetry Authors", - "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", - "license": "Apache-2.0", - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/open-telemetry/opentelemetry-js.git" - }, - "bugs": { - "url": "https://github.com/open-telemetry/opentelemetry-js/issues" - }, - "dependencies": { - "@opentelemetry/instrumentation-xml-http-request": "^0.18.0" - } -} diff --git a/package.json b/package.json index c2a636a83a..996a0acaf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opentelemetry-base", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry is a distributed tracing and stats collection framework.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -45,13 +45,13 @@ "author": "OpenTelemetry Authors", "license": "Apache-2.0", "devDependencies": { - "@commitlint/cli": "11.0.0", - "@commitlint/config-conventional": "11.0.0", - "@typescript-eslint/eslint-plugin": "4.19.0", - "@typescript-eslint/parser": "4.19.0", + "@commitlint/cli": "12.1.1", + "@commitlint/config-conventional": "12.1.1", + "@typescript-eslint/eslint-plugin": "4.22.0", + "@typescript-eslint/parser": "4.22.0", "beautify-benchmark": "0.2.4", "benchmark": "2.1.4", - "eslint": "7.22.0", + "eslint": "7.24.0", "eslint-config-airbnb-base": "14.2.1", "eslint-plugin-header": "3.1.1", "eslint-plugin-import": "2.22.1", @@ -61,8 +61,8 @@ "lerna": "3.22.1", "lerna-changelog": "1.0.1", "markdownlint-cli": "0.27.1", - "typescript": "4.2.3", - "update-ts-references": "2.0.0" + "typescript": "4.2.4", + "update-ts-references": "2.3.0" }, "husky": { "hooks": { diff --git a/packages/opentelemetry-api-metrics/package.json b/packages/opentelemetry-api-metrics/package.json index 2e72a122e3..3c351cfc85 100644 --- a/packages/opentelemetry-api-metrics/package.json +++ b/packages/opentelemetry-api-metrics/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/api-metrics", - "version": "0.18.0", + "version": "0.18.2", "description": "Public metrics API for OpenTelemetry", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -47,12 +47,13 @@ "publishConfig": { "access": "public" }, - "dependencies": { + "peerDependencies": { "@opentelemetry/api": "^1.0.0-rc.0" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/webpack-env": "1.16.0", "codecov": "3.8.1", "gts": "3.1.0", @@ -62,12 +63,12 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", - "ts-loader": "8.0.18", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0" } } diff --git a/packages/opentelemetry-api-metrics/src/version.ts b/packages/opentelemetry-api-metrics/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-api-metrics/src/version.ts +++ b/packages/opentelemetry-api-metrics/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-context-async-hooks/package.json b/packages/opentelemetry-context-async-hooks/package.json index d5f231febf..9d23b4c3aa 100644 --- a/packages/opentelemetry-context-async-hooks/package.json +++ b/packages/opentelemetry-context-async-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-async-hooks", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry AsyncHooks-based Context Manager", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -40,8 +40,9 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/shimmer": "1.0.1", "codecov": "3.8.1", "gts": "3.1.0", @@ -50,9 +51,9 @@ "rimraf": "3.0.2", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" }, - "dependencies": { + "peerDependencies": { "@opentelemetry/api": "^1.0.0-rc.0" } } diff --git a/packages/opentelemetry-context-async-hooks/src/AbstractAsyncHooksContextManager.ts b/packages/opentelemetry-context-async-hooks/src/AbstractAsyncHooksContextManager.ts index 1a4db11c93..0f540b62bb 100644 --- a/packages/opentelemetry-context-async-hooks/src/AbstractAsyncHooksContextManager.ts +++ b/packages/opentelemetry-context-async-hooks/src/AbstractAsyncHooksContextManager.ts @@ -146,10 +146,14 @@ export abstract class AbstractAsyncHooksContextManager const contextManager = this; return function (this: never, event: string) { const map = contextManager._getPatchMap(ee); - if (map?.[event] !== undefined) { - delete map[event]; + if (map !== undefined) { + if (arguments.length === 0) { + contextManager._createPatchMap(ee); + } else if (map[event] !== undefined) { + delete map[event]; + } } - return original.call(this, event); + return original.apply(this, arguments); }; } @@ -184,7 +188,7 @@ export abstract class AbstractAsyncHooksContextManager } private _createPatchMap(ee: EventEmitter): PatchMap { - const map = {}; + const map = Object.create(null); // eslint-disable-next-line @typescript-eslint/no-explicit-any (ee as any)[this._kOtListeners] = map; return map; diff --git a/packages/opentelemetry-context-async-hooks/src/version.ts b/packages/opentelemetry-context-async-hooks/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-context-async-hooks/src/version.ts +++ b/packages/opentelemetry-context-async-hooks/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts b/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts index 5926c9485b..ba0a169e91 100644 --- a/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts +++ b/packages/opentelemetry-context-async-hooks/test/AsyncHooksContextManager.test.ts @@ -415,6 +415,24 @@ for (const contextManagerClass of [ patchedEE.emit('test'); }); + it('should return current context and removeAllListeners (when enabled)', done => { + const ee = new EventEmitter(); + const context = ROOT_CONTEXT.setValue(key1, 1); + const patchedEE = contextManager.bind(ee, context); + const handler = () => { + assert.deepStrictEqual(contextManager.active(), context); + patchedEE.removeAllListeners(); + assert.strictEqual(patchedEE.listeners('test').length, 0); + assert.strictEqual(patchedEE.listeners('test1').length, 0); + return done(); + }; + patchedEE.on('test', handler); + patchedEE.on('test1', handler); + assert.strictEqual(patchedEE.listeners('test').length, 1); + assert.strictEqual(patchedEE.listeners('test1').length, 1); + patchedEE.emit('test'); + }); + /** * Even if asynchooks is disabled, the context propagation will * still works but it might be lost after any async op. diff --git a/packages/opentelemetry-context-zone-peer-dep/package.json b/packages/opentelemetry-context-zone-peer-dep/package.json index c4f393f150..bd61baf28d 100644 --- a/packages/opentelemetry-context-zone-peer-dep/package.json +++ b/packages/opentelemetry-context-zone-peer-dep/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-zone-peer-dep", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Context Zone with peer dependency for zone.js", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -41,9 +41,10 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "@types/zone.js": "0.5.12", @@ -56,23 +57,21 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "zone.js": "0.11.4" }, - "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "zone.js": "^0.10.2 || ^0.11.0" }, "sideEffects": false diff --git a/packages/opentelemetry-context-zone-peer-dep/src/version.ts b/packages/opentelemetry-context-zone-peer-dep/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-context-zone-peer-dep/src/version.ts +++ b/packages/opentelemetry-context-zone-peer-dep/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-context-zone/package.json b/packages/opentelemetry-context-zone/package.json index f2e71ed54a..52937e3599 100644 --- a/packages/opentelemetry-context-zone/package.json +++ b/packages/opentelemetry-context-zone/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/context-zone", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Context Zone", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -38,9 +38,9 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", + "@babel/core": "7.13.15", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "babel-loader": "8.2.2", @@ -50,21 +50,21 @@ "karma-chrome-launcher": "3.1.0", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, "dependencies": { - "@opentelemetry/context-zone-peer-dep": "^0.18.0", + "@opentelemetry/context-zone-peer-dep": "0.18.2", "zone.js": "^0.11.0" }, "sideEffects": true diff --git a/packages/opentelemetry-context-zone/src/version.ts b/packages/opentelemetry-context-zone/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-context-zone/src/version.ts +++ b/packages/opentelemetry-context-zone/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-core/package.json b/packages/opentelemetry-core/package.json index 8b7ad54900..f165ffc287 100644 --- a/packages/opentelemetry-core/package.json +++ b/packages/opentelemetry-core/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/core", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Core provides default and no-op implementations of the OpenTelemetry types for trace and metrics", "main": "build/src/index.js", "browser": { @@ -50,9 +50,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/propagator-b3": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/propagator-b3": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", @@ -64,19 +65,21 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", "semver": "^7.1.3" } } diff --git a/packages/opentelemetry-core/src/baggage/propagation/HttpBaggage.ts b/packages/opentelemetry-core/src/baggage/propagation/HttpBaggage.ts index 8b2b309ac3..9452623b99 100644 --- a/packages/opentelemetry-core/src/baggage/propagation/HttpBaggage.ts +++ b/packages/opentelemetry-core/src/baggage/propagation/HttpBaggage.ts @@ -25,6 +25,7 @@ import { TextMapSetter, createBaggage, baggageEntryMetadataFromString, + isInstrumentationSuppressed, } from '@opentelemetry/api'; const KEY_PAIR_SEPARATOR = '='; @@ -49,7 +50,7 @@ export const MAX_TOTAL_LENGTH = 8192; export class HttpBaggage implements TextMapPropagator { inject(context: Context, carrier: unknown, setter: TextMapSetter) { const baggage = getBaggage(context); - if (!baggage) return; + if (!baggage || isInstrumentationSuppressed(context)) return; const keyPairs = this._getKeyPairs(baggage) .filter((pair: string) => { return pair.length <= MAX_PER_NAME_VALUE_PAIRS; diff --git a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts index 735e8d2e44..d40b447798 100644 --- a/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts +++ b/packages/opentelemetry-core/src/context/propagation/HttpTraceContext.ts @@ -17,6 +17,8 @@ import { Context, getSpanContext, + isInstrumentationSuppressed, + isSpanContextValid, setSpanContext, SpanContext, TextMapGetter, @@ -73,7 +75,12 @@ export function parseTraceParent(traceParent: string): SpanContext | null { export class HttpTraceContext implements TextMapPropagator { inject(context: Context, carrier: unknown, setter: TextMapSetter) { const spanContext = getSpanContext(context); - if (!spanContext) return; + if ( + !spanContext || + isInstrumentationSuppressed(context) || + !isSpanContextValid(spanContext) + ) + return; const traceParent = `${VERSION}-${spanContext.traceId}-${ spanContext.spanId diff --git a/packages/opentelemetry-core/src/context/propagation/composite.ts b/packages/opentelemetry-core/src/context/propagation/composite.ts index a5a8216e78..507b407f24 100644 --- a/packages/opentelemetry-core/src/context/propagation/composite.ts +++ b/packages/opentelemetry-core/src/context/propagation/composite.ts @@ -41,7 +41,7 @@ export class CompositePropagator implements TextMapPropagator { this._propagators // older propagators may not have fields function, null check to be sure .map(p => (typeof p.fields === 'function' ? p.fields() : [])) - .reduce((x, y) => x.concat(y)) + .reduce((x, y) => x.concat(y), []) ) ); } diff --git a/packages/opentelemetry-core/src/index.ts b/packages/opentelemetry-core/src/index.ts index e0c6c75d20..9245f0148a 100644 --- a/packages/opentelemetry-core/src/index.ts +++ b/packages/opentelemetry-core/src/index.ts @@ -26,7 +26,6 @@ export * from './context/propagation/HttpTraceContext'; export * from './context/propagation/types'; export * from './baggage/propagation/HttpBaggage'; export * from './platform'; -export * from './trace/Plugin'; export * from './trace/sampler/AlwaysOffSampler'; export * from './trace/sampler/AlwaysOnSampler'; export * from './trace/sampler/ParentBasedSampler'; diff --git a/packages/opentelemetry-core/src/platform/BaseAbstractPlugin.ts b/packages/opentelemetry-core/src/platform/BaseAbstractPlugin.ts deleted file mode 100644 index 1bc19d9cc4..0000000000 --- a/packages/opentelemetry-core/src/platform/BaseAbstractPlugin.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Tracer, TracerProvider } from '@opentelemetry/api'; -import { Plugin, PluginConfig, PluginInternalFiles } from '../trace/Plugin'; - -/** This class represent the base to patch plugin. */ -export abstract class BaseAbstractPlugin implements Plugin { - abstract readonly moduleName: string; // required for internalFilesExports - supportedVersions?: string[]; - readonly version?: string; // required for internalFilesExports - - protected readonly _basedir?: string; // required for internalFilesExports - protected _config!: PluginConfig; - protected _internalFilesExports!: { [module: string]: unknown }; // output for internalFilesExports - protected readonly _internalFilesList?: PluginInternalFiles; // required for internalFilesExports - protected _moduleExports!: T; - protected _tracer!: Tracer; - - constructor( - protected readonly _tracerName: string, - protected readonly _tracerVersion?: string - ) {} - - disable(): void { - this.unpatch(); - } - - abstract enable( - moduleExports: T, - tracerProvider: TracerProvider, - config?: PluginConfig - ): T; - - protected abstract patch(): T; - protected abstract unpatch(): void; -} diff --git a/packages/opentelemetry-core/src/platform/browser/BasePlugin.ts b/packages/opentelemetry-core/src/platform/browser/BasePlugin.ts deleted file mode 100644 index b067deb828..0000000000 --- a/packages/opentelemetry-core/src/platform/browser/BasePlugin.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TracerProvider } from '@opentelemetry/api'; -import { Plugin, PluginConfig } from '../../trace/Plugin'; -import { BaseAbstractPlugin } from '../BaseAbstractPlugin'; - -/** This class represent the base to patch plugin. */ -export abstract class BasePlugin - extends BaseAbstractPlugin - implements Plugin { - enable( - moduleExports: T, - tracerProvider: TracerProvider, - config?: PluginConfig - ): T { - this._moduleExports = moduleExports; - this._tracer = tracerProvider.getTracer( - this._tracerName, - this._tracerVersion - ); - if (config) this._config = config; - return this.patch(); - } -} diff --git a/packages/opentelemetry-core/src/platform/browser/index.ts b/packages/opentelemetry-core/src/platform/browser/index.ts index 85023842c4..c18c66118d 100644 --- a/packages/opentelemetry-core/src/platform/browser/index.ts +++ b/packages/opentelemetry-core/src/platform/browser/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -export * from './BasePlugin'; export * from './environment'; export * from './hex-to-base64'; export * from './RandomIdGenerator'; diff --git a/packages/opentelemetry-core/src/platform/node/BasePlugin.ts b/packages/opentelemetry-core/src/platform/node/BasePlugin.ts deleted file mode 100644 index b711520d43..0000000000 --- a/packages/opentelemetry-core/src/platform/node/BasePlugin.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TracerProvider, diag } from '@opentelemetry/api'; -import { - Plugin, - PluginConfig, - PluginInternalFiles, - PluginInternalFilesVersion, -} from '../../trace/Plugin'; -import * as semver from 'semver'; -import * as path from 'path'; -import { BaseAbstractPlugin } from '../BaseAbstractPlugin'; - -/** This class represent the base to patch plugin. */ -export abstract class BasePlugin - extends BaseAbstractPlugin - implements Plugin { - enable( - moduleExports: T, - tracerProvider: TracerProvider, - config?: PluginConfig - ): T { - this._moduleExports = moduleExports; - this._tracer = tracerProvider.getTracer( - this._tracerName, - this._tracerVersion - ); - this._internalFilesExports = this._loadInternalFilesExports(); - if (config) this._config = config; - return this.patch(); - } - - disable(): void { - this.unpatch(); - } - - /** - * @TODO: To avoid circular dependencies, internal file loading functionality currently - * lives in BasePlugin. It is not meant to work in the browser and so this logic - * should eventually be moved somewhere else where it makes more sense. - * https://github.com/open-telemetry/opentelemetry-js/issues/285 - */ - private _loadInternalFilesExports(): PluginInternalFiles { - if (!this._internalFilesList) return {}; - if (!this.version || !this.moduleName || !this._basedir) { - // log here because internalFilesList was provided, so internal file loading - // was expected to be working - diag.debug( - 'loadInternalFiles failed because one of the required fields was missing: moduleName=%s, version=%s, basedir=%s', - this.moduleName, - this.version, - this._basedir - ); - return {}; - } - const extraModules: PluginInternalFiles = {}; - diag.debug('loadInternalFiles %o', this._internalFilesList); - Object.keys(this._internalFilesList).forEach(versionRange => { - this._loadInternalModule(versionRange, extraModules); - }); - if (Object.keys(extraModules).length === 0) { - diag.debug( - 'No internal files could be loaded for %s@%s', - this.moduleName, - this.version - ); - } - return extraModules; - } - - private _loadInternalModule( - versionRange: string, - outExtraModules: PluginInternalFiles - ): void { - if (semver.satisfies(this.version!, versionRange)) { - if (Object.keys(outExtraModules).length > 0) { - diag.warn( - 'Plugin for %s@%s, has overlap version range (%s) for internal files: %o', - this.moduleName, - this.version, - versionRange, - this._internalFilesList - ); - } - this._requireInternalFiles( - this._internalFilesList![versionRange], - this._basedir!, - outExtraModules - ); - } - } - - private _requireInternalFiles( - extraModulesList: PluginInternalFilesVersion, - basedir: string, - outExtraModules: PluginInternalFiles - ): void { - if (!extraModulesList) return; - Object.keys(extraModulesList).forEach(moduleName => { - try { - diag.debug('loading File %s', extraModulesList[moduleName]); - outExtraModules[moduleName] = require(path.join( - basedir, - extraModulesList[moduleName] - )); - } catch (e) { - diag.error( - 'Could not load internal file %s of module %s. Error: %s', - path.join(basedir, extraModulesList[moduleName]), - this.moduleName, - e.message - ); - } - }); - } - - protected abstract patch(): T; - - protected abstract unpatch(): void; -} diff --git a/packages/opentelemetry-core/src/platform/node/index.ts b/packages/opentelemetry-core/src/platform/node/index.ts index 85023842c4..c18c66118d 100644 --- a/packages/opentelemetry-core/src/platform/node/index.ts +++ b/packages/opentelemetry-core/src/platform/node/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -export * from './BasePlugin'; export * from './environment'; export * from './hex-to-base64'; export * from './RandomIdGenerator'; diff --git a/packages/opentelemetry-core/src/trace/Plugin.ts b/packages/opentelemetry-core/src/trace/Plugin.ts deleted file mode 100644 index a51543c65e..0000000000 --- a/packages/opentelemetry-core/src/trace/Plugin.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TracerProvider } from '@opentelemetry/api'; - -/** Interface Plugin to apply patch. */ -export interface Plugin { - /** - * Contains all supported versions. - * All versions must be compatible with [semver](https://semver.org/spec/v2.0.0.html) format. - * If the version is not supported, we won't apply instrumentation patch (see `enable` method). - * If omitted, all versions of the module will be patched. - */ - supportedVersions?: string[]; - - /** - * Name of the module that the plugin instrument. - */ - moduleName: string; - - /** - * Method that enables the instrumentation patch. - * @param moduleExports The value of the `module.exports` property that would - * normally be exposed by the required module. ex: `http`, `https` etc. - * @param TracerProvider a tracer provider. - * @param [config] an object to configure the plugin. - */ - enable( - moduleExports: T, - TracerProvider: TracerProvider, - config?: PluginConfig - ): T; - - /** Method to disable the instrumentation */ - disable(): void; -} - -export interface PluginConfig { - /** - * Whether to enable the plugin. - * @default true - */ - enabled?: boolean; - - /** - * Path of the trace plugin to load. - * @default '@opentelemetry/plugin-http' in case of http. - */ - path?: string; - - /** - * Request methods that match any string in ignoreMethods will not be traced. - */ - ignoreMethods?: string[]; - - /** - * URLs that partially match any regex in ignoreUrls will not be traced. - * In addition, URLs that are _exact matches_ of strings in ignoreUrls will - * also not be traced. - */ - ignoreUrls?: Array; - - /** - * List of internal files that need patch and are not exported by - * default. - */ - internalFilesExports?: PluginInternalFiles; - - /** - * If true, additional information about query parameters and - * results will be attached (as `attributes`) to spans representing - * database operations. - */ - enhancedDatabaseReporting?: boolean; -} - -export interface PluginInternalFilesVersion { - [pluginName: string]: string; -} - -/** - * Each key should be the name of the module to trace, and its value - * a mapping of a property name to a internal plugin file name. - */ -export interface PluginInternalFiles { - [versions: string]: PluginInternalFilesVersion; -} diff --git a/packages/opentelemetry-core/src/utils/environment.ts b/packages/opentelemetry-core/src/utils/environment.ts index c353a38673..e50318db14 100644 --- a/packages/opentelemetry-core/src/utils/environment.ts +++ b/packages/opentelemetry-core/src/utils/environment.ts @@ -43,7 +43,10 @@ function isEnvVarANumber(key: unknown): key is keyof ENVIRONMENT_NUMBERS { ); } -const ENVIRONMENT_LISTS_KEYS = ['OTEL_NO_PATCH_MODULES'] as const; +const ENVIRONMENT_LISTS_KEYS = [ + 'OTEL_NO_PATCH_MODULES', + 'OTEL_PROPAGATORS', +] as const; type ENVIRONMENT_LISTS = { [K in typeof ENVIRONMENT_LISTS_KEYS[number]]?: string[]; @@ -83,21 +86,22 @@ export const DEFAULT_ENVIRONMENT: Required = { HOSTNAME: '', KUBERNETES_SERVICE_HOST: '', NAMESPACE: '', + OTEL_BSP_EXPORT_TIMEOUT: 30000, + OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 512, + OTEL_BSP_MAX_QUEUE_SIZE: 2048, + OTEL_BSP_SCHEDULE_DELAY: 5000, OTEL_EXPORTER_JAEGER_AGENT_HOST: '', OTEL_EXPORTER_JAEGER_ENDPOINT: '', OTEL_EXPORTER_JAEGER_PASSWORD: '', OTEL_EXPORTER_JAEGER_USER: '', OTEL_LOG_LEVEL: DiagLogLevel.INFO, OTEL_NO_PATCH_MODULES: [], + OTEL_PROPAGATORS: ['tracecontext', 'baggage'], OTEL_RESOURCE_ATTRIBUTES: '', OTEL_SAMPLING_PROBABILITY: 1, - OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 1000, - OTEL_SPAN_EVENT_COUNT_LIMIT: 1000, - OTEL_SPAN_LINK_COUNT_LIMIT: 1000, - OTEL_BSP_EXPORT_TIMEOUT: 30000, - OTEL_BSP_MAX_EXPORT_BATCH_SIZE: 512, - OTEL_BSP_MAX_QUEUE_SIZE: 2048, - OTEL_BSP_SCHEDULE_DELAY: 5000, + OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT: 128, + OTEL_SPAN_EVENT_COUNT_LIMIT: 128, + OTEL_SPAN_LINK_COUNT_LIMIT: 128, }; /** diff --git a/packages/opentelemetry-core/src/version.ts b/packages/opentelemetry-core/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-core/src/version.ts +++ b/packages/opentelemetry-core/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts index a732a471f9..ca2bfabb7a 100644 --- a/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts +++ b/packages/opentelemetry-core/test/context/HttpTraceContext.test.ts @@ -17,12 +17,15 @@ import { defaultTextMapGetter, defaultTextMapSetter, - SpanContext, - TraceFlags, getSpanContext, + INVALID_SPANID, + INVALID_TRACEID, + ROOT_CONTEXT, setSpanContext, + SpanContext, + suppressInstrumentation, + TraceFlags, } from '@opentelemetry/api'; -import { ROOT_CONTEXT } from '@opentelemetry/api'; import * as assert from 'assert'; import { HttpTraceContext, @@ -78,6 +81,39 @@ describe('HttpTraceContext', () => { ); assert.deepStrictEqual(carrier[TRACE_STATE_HEADER], 'foo=bar,baz=qux'); }); + + it('should not set traceparent and tracestate header if instrumentation is suppressed', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + httpTraceContext.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + assert.strictEqual(carrier[TRACE_PARENT_HEADER], undefined); + assert.strictEqual(carrier[TRACE_STATE_HEADER], undefined); + }); + + it('should ignore invalid span context', () => { + const spanContext: SpanContext = { + traceId: INVALID_TRACEID, + spanId: INVALID_SPANID, + traceFlags: TraceFlags.NONE, + traceState: new TraceState('foo=bar,baz=qux'), + }; + + httpTraceContext.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + assert.strictEqual(carrier[TRACE_PARENT_HEADER], undefined); + assert.strictEqual(carrier[TRACE_STATE_HEADER], undefined); + }); }); describe('.extract()', () => { diff --git a/packages/opentelemetry-core/test/context/composite.test.ts b/packages/opentelemetry-core/test/context/composite.test.ts index 52114aeeb9..6addee1ff2 100644 --- a/packages/opentelemetry-core/test/context/composite.test.ts +++ b/packages/opentelemetry-core/test/context/composite.test.ts @@ -30,7 +30,8 @@ import { RandomIdGenerator, } from '../../src'; import { - B3MultiPropagator, + B3Propagator, + B3InjectEncoding, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, @@ -69,7 +70,10 @@ describe('Composite Propagator', () => { it('should inject context using all configured propagators', () => { const composite = new CompositePropagator({ - propagators: [new B3MultiPropagator(), new HttpTraceContext()], + propagators: [ + new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }), + new HttpTraceContext(), + ], }); composite.inject(ctxWithSpanContext, carrier, defaultTextMapSetter); @@ -111,7 +115,10 @@ describe('Composite Propagator', () => { it('should extract context using all configured propagators', () => { const composite = new CompositePropagator({ - propagators: [new B3MultiPropagator(), new HttpTraceContext()], + propagators: [ + new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }), + new HttpTraceContext(), + ], }); const spanContext = getSpanContext( composite.extract(ROOT_CONTEXT, carrier, defaultTextMapGetter) diff --git a/packages/opentelemetry-core/test/index-webpack.ts b/packages/opentelemetry-core/test/index-webpack.ts index 47db37c3f9..dce3150b40 100644 --- a/packages/opentelemetry-core/test/index-webpack.ts +++ b/packages/opentelemetry-core/test/index-webpack.ts @@ -14,12 +14,7 @@ * limitations under the License. */ const testsContextCommon = require.context('.', true, /test$/); -testsContextCommon.keys().forEach(key => { - if (key.indexOf('./platform/BasePlugin.test') >= 0) { - return function () {}; - } - return testsContextCommon(key); -}); +testsContextCommon.keys().forEach(testsContextCommon); const srcContext = require.context('.', true, /src$/); srcContext.keys().forEach(srcContext); diff --git a/packages/opentelemetry-core/test/platform/BasePlugin.test.ts b/packages/opentelemetry-core/test/platform/BasePlugin.test.ts deleted file mode 100644 index 092ad7393a..0000000000 --- a/packages/opentelemetry-core/test/platform/BasePlugin.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { NoopTracerProvider } from '@opentelemetry/api'; -import * as assert from 'assert'; -import * as path from 'path'; -import { BasePlugin } from '../../src'; -import * as types from '../trace/fixtures/test-package/foo/bar/internal'; - -const provider = new NoopTracerProvider(); -describe('BasePlugin', () => { - describe('internalFilesLoader', () => { - it('should load internally exported files', () => { - const testPackage = require('../trace/fixtures/test-package'); - const plugin = new TestPlugin(); - assert.doesNotThrow(() => { - plugin.enable(testPackage, provider); - }); - - // @TODO: https://github.com/open-telemetry/opentelemetry-js/issues/285 - if (typeof process !== 'undefined' && process.release.name === 'node') { - assert.ok(plugin['_internalFilesExports']); - assert.strictEqual( - (plugin['_internalFilesExports'] - .internal as typeof types).internallyExportedFunction(), - true - ); - assert.strictEqual( - plugin['_internalFilesExports'].expectUndefined, - undefined - ); - assert.strictEqual( - (plugin['_moduleExports']![ - 'externallyExportedFunction' - ] as Function)(), - true - ); - } else { - assert.ok(true, 'Internal file loading is not tested in the browser'); - } - }); - }); -}); - -class TestPlugin extends BasePlugin<{ [key: string]: Function }> { - readonly moduleName = 'test-package'; - readonly version = '0.1.0'; - readonly _basedir = basedir; - - constructor() { - super('test-package.opentelemetry'); - } - - protected readonly _internalFilesList = { - '0.1.0': { - internal: 'foo/bar/internal.js', - }, - '^1.0.0': { - expectUndefined: 'foo/bar/internal.js', - }, - }; - - protected patch(): { [key: string]: Function } { - return this._moduleExports; - } - protected unpatch(): void {} -} - -const basedir = path.dirname(require.resolve('../trace/fixtures/test-package')); diff --git a/packages/opentelemetry-core/test/platform/browser/BasePlugin.test.ts b/packages/opentelemetry-core/test/platform/browser/BasePlugin.test.ts deleted file mode 100644 index 303e5b05aa..0000000000 --- a/packages/opentelemetry-core/test/platform/browser/BasePlugin.test.ts +++ /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. - */ - -import { NOOP_TRACER, NoopTracerProvider } from '@opentelemetry/api'; -import * as assert from 'assert'; -import { BasePlugin } from '../../../src'; - -const provider = new NoopTracerProvider(); -describe('BasePlugin', () => { - describe('enable', () => { - it('should enable plugin', () => { - const moduleExports = { foo: function () {} }; - const plugin = new TestPlugin('foo', '1'); - const patch = plugin.enable(moduleExports, provider); - - assert.strictEqual(plugin['_tracer'], NOOP_TRACER); - assert.strictEqual(plugin['_tracerName'], 'foo'); - assert.strictEqual(plugin['_tracerVersion'], '1'); - assert.strictEqual(patch, moduleExports); - }); - }); -}); - -class TestPlugin extends BasePlugin<{ [key: string]: Function }> { - readonly moduleName = 'test-package'; - readonly version = '0.1.0'; - - patch(): { [key: string]: Function } { - return this._moduleExports; - } - - protected unpatch(): void {} -} diff --git a/packages/opentelemetry-core/test/utils/url.test.ts b/packages/opentelemetry-core/test/utils/url.test.ts index 8c9582989d..91ae9b75c5 100644 --- a/packages/opentelemetry-core/test/utils/url.test.ts +++ b/packages/opentelemetry-core/test/utils/url.test.ts @@ -23,7 +23,7 @@ const urlNotIgnored = 'url should NOT be ignored'; const urlToTest = 'http://myaddress.com/somepath'; -describe('BasePlugin - Utils', () => { +describe('Core - Utils - url', () => { describe('isUrlIgnored', () => { describe('when ignored urls are undefined', () => { it('should return false', () => { diff --git a/packages/opentelemetry-exporter-collector-grpc/package.json b/packages/opentelemetry-exporter-collector-grpc/package.json index 3b14d4fda5..3f7c9024e5 100644 --- a/packages/opentelemetry-exporter-collector-grpc/package.json +++ b/packages/opentelemetry-exporter-collector-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-collector-grpc", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -45,10 +45,11 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", - "@opentelemetry/api-metrics": "^0.18.0", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/api-metrics": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "cpx": "1.5.0", @@ -56,20 +57,22 @@ "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@grpc/proto-loader": "^0.5.4", - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-collector": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "grpc": "^1.24.2" + "@grpc/grpc-js": "^1.2.12", + "@grpc/proto-loader": "^0.6.0", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/exporter-collector": "0.18.2", + "@opentelemetry/metrics": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/tracing": "0.18.2" } } diff --git a/packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts b/packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts index 9c7048c188..c01264cb94 100644 --- a/packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts +++ b/packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts @@ -19,7 +19,7 @@ import { CollectorExporterBase, collectorTypes, } from '@opentelemetry/exporter-collector'; -import type { Metadata } from 'grpc'; +import type { Metadata } from '@grpc/grpc-js'; import { CollectorExporterConfigNode, GRPCQueueItem, diff --git a/packages/opentelemetry-exporter-collector-grpc/src/types.ts b/packages/opentelemetry-exporter-collector-grpc/src/types.ts index aa8f98a0a4..3d8dda8dca 100644 --- a/packages/opentelemetry-exporter-collector-grpc/src/types.ts +++ b/packages/opentelemetry-exporter-collector-grpc/src/types.ts @@ -15,7 +15,7 @@ */ import { collectorTypes } from '@opentelemetry/exporter-collector'; -import * as grpc from 'grpc'; +import * as grpc from '@grpc/grpc-js'; /** * Queue item to be used to save temporary spans/metrics in case the GRPC service diff --git a/packages/opentelemetry-exporter-collector-grpc/src/util.ts b/packages/opentelemetry-exporter-collector-grpc/src/util.ts index 7da27e95bc..72ea1c7369 100644 --- a/packages/opentelemetry-exporter-collector-grpc/src/util.ts +++ b/packages/opentelemetry-exporter-collector-grpc/src/util.ts @@ -17,7 +17,7 @@ import { diag } from '@opentelemetry/api'; import * as protoLoader from '@grpc/proto-loader'; import { collectorTypes } from '@opentelemetry/exporter-collector'; -import * as grpc from 'grpc'; +import * as grpc from '@grpc/grpc-js'; import * as path from 'path'; import { @@ -82,7 +82,7 @@ export function send( collector.serviceClient.export( serviceRequest, - collector.metadata, + collector.metadata || new grpc.Metadata(), (err: collectorTypes.ExportServiceError) => { if (err) { diag.error('Service request', serviceRequest); diff --git a/packages/opentelemetry-exporter-collector-grpc/src/version.ts b/packages/opentelemetry-exporter-collector-grpc/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-collector-grpc/src/version.ts +++ b/packages/opentelemetry-exporter-collector-grpc/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-exporter-collector-grpc/test/CollectorMetricExporter.test.ts b/packages/opentelemetry-exporter-collector-grpc/test/CollectorMetricExporter.test.ts index 7f2a45f51a..59677eb525 100644 --- a/packages/opentelemetry-exporter-collector-grpc/test/CollectorMetricExporter.test.ts +++ b/packages/opentelemetry-exporter-collector-grpc/test/CollectorMetricExporter.test.ts @@ -25,7 +25,7 @@ import { collectorTypes } from '@opentelemetry/exporter-collector'; import * as metrics from '@opentelemetry/metrics'; import * as assert from 'assert'; import * as fs from 'fs'; -import * as grpc from 'grpc'; +import * as grpc from '@grpc/grpc-js'; import * as path from 'path'; import * as sinon from 'sinon'; import { CollectorMetricExporter } from '../src'; @@ -109,9 +109,10 @@ const testCollectorMetricExporter = (params: TestParams) => ] ) : grpc.ServerCredentials.createInsecure(); - server.bind(address, credentials); - server.start(); - done(); + server.bindAsync(address, credentials, () => { + server.start(); + done(); + }); }); }); diff --git a/packages/opentelemetry-exporter-collector-grpc/test/CollectorTraceExporter.test.ts b/packages/opentelemetry-exporter-collector-grpc/test/CollectorTraceExporter.test.ts index 36c752ea18..a1a38db5f3 100644 --- a/packages/opentelemetry-exporter-collector-grpc/test/CollectorTraceExporter.test.ts +++ b/packages/opentelemetry-exporter-collector-grpc/test/CollectorTraceExporter.test.ts @@ -24,7 +24,7 @@ import { import * as assert from 'assert'; import * as fs from 'fs'; -import * as grpc from 'grpc'; +import * as grpc from '@grpc/grpc-js'; import * as path from 'path'; import * as sinon from 'sinon'; import { CollectorTraceExporter } from '../src'; @@ -104,9 +104,10 @@ const testCollectorExporter = (params: TestParams) => ] ) : grpc.ServerCredentials.createInsecure(); - server.bind(address, credentials); - server.start(); - done(); + server.bindAsync(address, credentials, () => { + server.start(); + done(); + }); }); }); diff --git a/packages/opentelemetry-exporter-collector-grpc/test/helper.ts b/packages/opentelemetry-exporter-collector-grpc/test/helper.ts index 607a8ad729..8d2c0bb140 100644 --- a/packages/opentelemetry-exporter-collector-grpc/test/helper.ts +++ b/packages/opentelemetry-exporter-collector-grpc/test/helper.ts @@ -27,7 +27,7 @@ import * as metrics from '@opentelemetry/metrics'; import { Resource } from '@opentelemetry/resources'; import { ReadableSpan } from '@opentelemetry/tracing'; import * as assert from 'assert'; -import * as grpc from 'grpc'; +import * as grpc from '@grpc/grpc-js'; const meterProvider = new metrics.MeterProvider({ interval: 30000, diff --git a/packages/opentelemetry-exporter-collector-proto/package.json b/packages/opentelemetry-exporter-collector-proto/package.json index 4535314792..d09f13180b 100644 --- a/packages/opentelemetry-exporter-collector-proto/package.json +++ b/packages/opentelemetry-exporter-collector-proto/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-collector-proto", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -45,10 +45,11 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", - "@opentelemetry/api-metrics": "^0.18.0", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/api-metrics": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "cpx": "1.5.0", @@ -56,20 +57,22 @@ "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@grpc/proto-loader": "^0.5.4", - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/exporter-collector": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@grpc/proto-loader": "^0.6.0", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/exporter-collector": "0.18.2", + "@opentelemetry/metrics": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "protobufjs": "^6.9.0" } } diff --git a/packages/opentelemetry-exporter-collector-proto/src/version.ts b/packages/opentelemetry-exporter-collector-proto/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-collector-proto/src/version.ts +++ b/packages/opentelemetry-exporter-collector-proto/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-exporter-collector/README.md b/packages/opentelemetry-exporter-collector/README.md index c0d3ee32c5..4b60a4d4ea 100644 --- a/packages/opentelemetry-exporter-collector/README.md +++ b/packages/opentelemetry-exporter-collector/README.md @@ -31,10 +31,14 @@ const collectorOptions = { const provider = new WebTracerProvider(); const exporter = new CollectorTraceExporter(collectorOptions); provider.addSpanProcessor(new BatchSpanProcessor(exporter, { - // send spans as soon as we have this many - bufferSize: 10, - // send spans if we have buffered spans older than this - bufferTimeout: 500, + // The maximum queue size. After the size is reached spans are dropped. + maxQueueSize: 100, + // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. + maxExportBatchSize: 10, + // The interval between two consecutive exports + scheduledDelayMillis: 500, + // How long the export can run before it is cancelled + exportTimeoutMillis: 30000, })); provider.register(); @@ -85,10 +89,10 @@ const collectorOptions = { const provider = new BasicTracerProvider(); const exporter = new CollectorTraceExporter(collectorOptions); provider.addSpanProcessor(new BatchSpanProcessor(exporter, { - // send spans as soon as we have this many - bufferSize: 1000, - // send spans if we have buffered spans older than this - bufferTimeout: 30000, + // The maximum queue size. After the size is reached spans are dropped. + maxQueueSize: 1000, + // The interval between two consecutive exports + scheduledDelayMillis: 30000, })); provider.register(); diff --git a/packages/opentelemetry-exporter-collector/package.json b/packages/opentelemetry-exporter-collector/package.json index c6ea0332d6..642558f81c 100644 --- a/packages/opentelemetry-exporter-collector/package.json +++ b/packages/opentelemetry-exporter-collector/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-collector", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Collector Exporter allows user to send collected traces to the OpenTelemetry Collector", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -48,9 +48,10 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", + "@opentelemetry/api": "^1.0.0-rc.0", + "@babel/core": "7.13.15", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "babel-loader": "8.2.2", @@ -63,25 +64,27 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/api-metrics": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/metrics": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/tracing": "0.18.2" } } diff --git a/packages/opentelemetry-exporter-collector/src/version.ts b/packages/opentelemetry-exporter-collector/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-collector/src/version.ts +++ b/packages/opentelemetry-exporter-collector/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-exporter-jaeger/package.json b/packages/opentelemetry-exporter-jaeger/package.json index c9801d6273..23fef83d13 100644 --- a/packages/opentelemetry-exporter-jaeger/package.json +++ b/packages/opentelemetry-exporter-jaeger/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-jaeger", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Exporter Jaeger allows user to send collected traces to Jaeger", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -40,9 +40,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/resources": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/resources": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", @@ -50,15 +51,17 @@ "nock": "12.0.3", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "jaeger-client": "^3.15.0" } } diff --git a/packages/opentelemetry-exporter-jaeger/src/version.ts b/packages/opentelemetry-exporter-jaeger/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-jaeger/src/version.ts +++ b/packages/opentelemetry-exporter-jaeger/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-exporter-prometheus/package.json b/packages/opentelemetry-exporter-prometheus/package.json index 91272132f7..09abcd0aef 100644 --- a/packages/opentelemetry-exporter-prometheus/package.json +++ b/packages/opentelemetry-exporter-prometheus/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-prometheus", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Exporter Prometheus provides a metrics endpoint for Prometheus", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -39,23 +39,26 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0" + "@opentelemetry/api-metrics": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/metrics": "0.18.2" } } diff --git a/packages/opentelemetry-exporter-prometheus/src/version.ts b/packages/opentelemetry-exporter-prometheus/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-prometheus/src/version.ts +++ b/packages/opentelemetry-exporter-prometheus/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-exporter-zipkin/package.json b/packages/opentelemetry-exporter-zipkin/package.json index 220972bbf6..daa239e71e 100644 --- a/packages/opentelemetry-exporter-zipkin/package.json +++ b/packages/opentelemetry-exporter-zipkin/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/exporter-zipkin", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Zipkin Exporter allows the user to send collected traces to Zipkin.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -46,9 +46,10 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "babel-loader": "8.2.2", @@ -60,24 +61,26 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nock": "12.0.3", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/core": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/tracing": "0.18.2" } } diff --git a/packages/opentelemetry-exporter-zipkin/src/version.ts b/packages/opentelemetry-exporter-zipkin/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-exporter-zipkin/src/version.ts +++ b/packages/opentelemetry-exporter-zipkin/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-grpc-utils/.npmignore b/packages/opentelemetry-grpc-utils/.npmignore deleted file mode 100644 index 9505ba9450..0000000000 --- a/packages/opentelemetry-grpc-utils/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/coverage -/doc -/test diff --git a/packages/opentelemetry-grpc-utils/README.md b/packages/opentelemetry-grpc-utils/README.md deleted file mode 100644 index 5d244a9dfe..0000000000 --- a/packages/opentelemetry-grpc-utils/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenTelemetry gRPC Instrumentation for Node.js - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![devDependencies][devDependencies-image]][devDependencies-url] -[![Apache License][license-image]][license-image] - -This module provides automatic instrumentation for [`grpc`](https://grpc.github.io/grpc/node/). Currently, version [`1.x`](https://www.npmjs.com/package/grpc?activeTab=versions) of the Node.js gRPC library is supported. - -For automatic instrumentation see the -[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node) package. - -## Installation - -```sh -npm install --save @opentelemetry/plugin-grpc -``` - -## Usage - -OpenTelemetry gRPC Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems when working with [gRPC](https://www.npmjs.com/package/grpc). - -To load a specific plugin (**gRPC** in this case), specify it in the Node Tracer's configuration. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider({ - plugins: { - grpc: { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-grpc', - } - } -}); -``` - -To load all of the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider(); -``` - -See [examples/grpc](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc) for a short example. - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-instrumentation-grpc -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-instrumentation-grpc -[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-instrumentation-grpc&type=dev -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-grpc&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-grpc -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-grpc.svg diff --git a/packages/opentelemetry-grpc-utils/package.json b/packages/opentelemetry-grpc-utils/package.json deleted file mode 100644 index e566398f22..0000000000 --- a/packages/opentelemetry-grpc-utils/package.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "@opentelemetry/grpc-utils", - "version": "0.18.0", - "private": true, - "description": "OpenTelemetry grpc plugin utility functions.", - "main": "build/src/index.js", - "types": "build/src/index.d.ts", - "repository": "open-telemetry/opentelemetry-js", - "scripts": { - "compile": "tsc --build", - "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", - "tdd": "npm run test -- --watch-extensions ts --watch", - "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" - }, - "keywords": [ - "opentelemetry", - "grpc", - "nodejs", - "tracing", - "profiling", - "plugin" - ], - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - }, - "files": [ - "build/src/**/*.js", - "build/src/**/*.d.ts", - "doc", - "LICENSE", - "README.md" - ], - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "@grpc/grpc-js": "1.2.12", - "@grpc/proto-loader": "0.5.6", - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@types/mocha": "8.2.2", - "@types/node": "14.14.35", - "@types/semver": "7.3.4", - "@types/shimmer": "1.0.1", - "@types/sinon": "9.0.11", - "codecov": "3.8.1", - "grpc": "1.24.6", - "gts": "3.1.0", - "mocha": "7.2.0", - "node-pre-gyp": "0.17.0", - "nyc": "15.1.0", - "rimraf": "3.0.2", - "semver": "7.3.5", - "sinon": "9.2.4", - "ts-mocha": "8.0.0", - "ts-node": "9.1.1", - "typescript": "4.2.3" - }, - "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "shimmer": "1.2.1" - } -} diff --git a/packages/opentelemetry-grpc-utils/test/fixtures/grpc-test.proto b/packages/opentelemetry-grpc-utils/test/fixtures/grpc-test.proto deleted file mode 100644 index 4949dd5e0d..0000000000 --- a/packages/opentelemetry-grpc-utils/test/fixtures/grpc-test.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package pkg_test; - -service GrpcTester { - rpc UnaryMethod (TestRequest) returns (TestReply) {} - rpc camelCaseMethod (TestRequest) returns (TestReply) {} - rpc ClientStreamMethod (stream TestRequest) returns (TestReply) {} - rpc ServerStreamMethod (TestRequest) returns (stream TestReply) {} - rpc BidiStreamMethod (stream TestRequest) returns (stream TestReply) {} -} - -message TestRequest { - int32 num = 1; -} - -message TestReply { - int32 num = 1; -} diff --git a/packages/opentelemetry-grpc-utils/test/grpcUtils.test.ts b/packages/opentelemetry-grpc-utils/test/grpcUtils.test.ts deleted file mode 100644 index 4fd0161f09..0000000000 --- a/packages/opentelemetry-grpc-utils/test/grpcUtils.test.ts +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - context, - NoopTracerProvider, - SpanKind, - propagation, - getSpan, - setSpan, -} from '@opentelemetry/api'; -import { - HttpTraceContext, - BasePlugin, - PluginConfig, -} from '@opentelemetry/core'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ContextManager } from '@opentelemetry/api'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import * as assert from 'assert'; -import * as protoLoader from '@grpc/proto-loader'; -import type * as grpcNapi from 'grpc'; -import type * as grpcJs from '@grpc/grpc-js'; -import { assertPropagation, assertSpan } from './utils/assertionUtils'; -import { promisify } from 'util'; - -const PROTO_PATH = - process.cwd() + - '/node_modules/@opentelemetry/grpc-utils/test/fixtures/grpc-test.proto'; -const memoryExporter = new InMemorySpanExporter(); - -const options = { - keepCase: true, - longs: String, - enums: String, - defaults: true, - oneofs: true, -}; - -interface TestRequestResponse { - num: number; -} - -type ServiceError = grpcNapi.ServiceError | grpcJs.ServiceError; -type Client = grpcNapi.Client | grpcJs.Client; -type Server = grpcNapi.Server | grpcJs.Server; -type ServerUnaryCall = - | grpcNapi.ServerUnaryCall - | grpcJs.ServerUnaryCall; -type RequestCallback = grpcJs.requestCallback; -type ServerReadableStream = - | grpcNapi.ServerReadableStream - | grpcJs.ServerReadableStream; -type ServerWriteableStream = - | grpcNapi.ServerWriteableStream - | grpcJs.ServerWritableStream; -type ServerDuplexStream = - | grpcNapi.ServerDuplexStream - | grpcJs.ServerDuplexStream; -type Metadata = grpcNapi.Metadata | grpcJs.Metadata; - -type TestGrpcClient = (typeof grpcJs | typeof grpcNapi)['Client'] & { - unaryMethod: any; - UnaryMethod: any; - camelCaseMethod: any; - clientStreamMethod: any; - serverStreamMethod: any; - bidiStreamMethod: any; -}; - -interface TestGrpcCall { - description: string; - methodName: string; - method: Function; - request: TestRequestResponse | TestRequestResponse[]; - result: TestRequestResponse | TestRequestResponse[]; - metadata?: Metadata; -} - -// Compare two arrays using an equal function f -const arrayIsEqual = (f: any) => ([x, ...xs]: any) => ([y, ...ys]: any): any => - x === undefined && y === undefined - ? true - : Boolean(f(x)(y)) && arrayIsEqual(f)(xs)(ys); - -// Return true if two requests has the same num value -const requestEqual = (x: TestRequestResponse) => (y: TestRequestResponse) => - x.num !== undefined && x.num === y.num; - -// Check if its equal requests or array of requests -const checkEqual = (x: TestRequestResponse | TestRequestResponse[]) => ( - y: TestRequestResponse | TestRequestResponse[] -) => - x instanceof Array && y instanceof Array - ? arrayIsEqual(requestEqual)(x as any)(y as any) - : !(x instanceof Array) && !(y instanceof Array) - ? requestEqual(x)(y) - : false; - -export const runTests = ( - plugin: BasePlugin, - moduleName: string, - grpc: typeof grpcNapi | typeof grpcJs, - grpcPort: number -) => { - const MAX_ERROR_STATUS = grpc.status.UNAUTHENTICATED; - - const grpcClient = { - unaryMethod: ( - client: TestGrpcClient, - request: TestRequestResponse, - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - return client.unaryMethod( - request, - metadata, - (err: ServiceError, response: TestRequestResponse) => { - if (err) { - reject(err); - } else { - resolve(response); - } - } - ); - }); - }, - - UnaryMethod: ( - client: TestGrpcClient, - request: TestRequestResponse, - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - return client.UnaryMethod( - request, - metadata, - (err: ServiceError, response: TestRequestResponse) => { - if (err) { - reject(err); - } else { - resolve(response); - } - } - ); - }); - }, - - camelCaseMethod: ( - client: TestGrpcClient, - request: TestRequestResponse, - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - return client.camelCaseMethod( - request, - metadata, - (err: ServiceError, response: TestRequestResponse) => { - if (err) { - reject(err); - } else { - resolve(response); - } - } - ); - }); - }, - - clientStreamMethod: ( - client: TestGrpcClient, - request: TestRequestResponse[], - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - const writeStream = client.clientStreamMethod( - metadata, - (err: ServiceError, response: TestRequestResponse) => { - if (err) { - reject(err); - } else { - resolve(response); - } - } - ); - - request.forEach(element => { - writeStream.write(element); - }); - writeStream.end(); - }); - }, - - serverStreamMethod: ( - client: TestGrpcClient, - request: TestRequestResponse, - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - const result: TestRequestResponse[] = []; - const readStream = client.serverStreamMethod(request, metadata); - - readStream.on('data', (data: TestRequestResponse) => { - result.push(data); - }); - readStream.on('error', (err: ServiceError) => { - reject(err); - }); - readStream.on('end', () => { - resolve(result); - }); - }); - }, - - bidiStreamMethod: ( - client: TestGrpcClient, - request: TestRequestResponse[], - metadata: Metadata = new grpc.Metadata() - ): Promise => { - return new Promise((resolve, reject) => { - const result: TestRequestResponse[] = []; - const bidiStream = client.bidiStreamMethod(metadata); - - bidiStream.on('data', (data: TestRequestResponse) => { - result.push(data); - }); - - request.forEach(element => { - bidiStream.write(element); - }); - - bidiStream.on('error', (err: ServiceError) => { - reject(err); - }); - - bidiStream.on('end', () => { - resolve(result); - }); - - bidiStream.end(); - }); - }, - }; - - let server: Server; - let client: Client; - - const replicate = (request: TestRequestResponse) => { - const result: TestRequestResponse[] = []; - for (let i = 0; i < request.num; i++) { - result.push(request); - } - return result; - }; - - async function startServer( - grpc: typeof grpcJs | typeof grpcNapi, - proto: any - ) { - const server = new grpc.Server(); - - function getError(msg: string, code: number): ServiceError | null { - const err: ServiceError = { - ...new Error(msg), - name: msg, - message: msg, - code, - details: msg, - }; - return err; - } - - server.addService(proto.GrpcTester.service, { - // An error is emitted every time - // request.num <= MAX_ERROR_STATUS = (status.UNAUTHENTICATED) - // in those cases, erro.code = request.num - - // This method returns the request - unaryMethod(call: ServerUnaryCall, callback: RequestCallback) { - call.request.num <= MAX_ERROR_STATUS - ? callback( - getError( - 'Unary Method Error', - call.request.num - ) as grpcJs.ServiceError - ) - : callback(null, { num: call.request.num }); - }, - - // This method returns the request - camelCaseMethod(call: ServerUnaryCall, callback: RequestCallback) { - call.request.num <= MAX_ERROR_STATUS - ? callback( - getError( - 'Unary Method Error', - call.request.num - ) as grpcJs.ServiceError - ) - : callback(null, { num: call.request.num }); - }, - - // This method sums the requests - clientStreamMethod( - call: ServerReadableStream, - callback: RequestCallback - ) { - let sum = 0; - let hasError = false; - let code = grpc.status.OK; - call.on('data', (data: TestRequestResponse) => { - sum += data.num; - if (data.num <= MAX_ERROR_STATUS) { - hasError = true; - code = data.num; - } - }); - call.on('end', () => { - hasError - ? callback(getError('Client Stream Method Error', code) as any) - : callback(null, { num: sum }); - }); - }, - - // This method returns an array that replicates the request, request.num of - // times - serverStreamMethod: (call: ServerWriteableStream) => { - const result = replicate(call.request); - - if (call.request.num <= MAX_ERROR_STATUS) { - call.emit( - 'error', - getError('Server Stream Method Error', call.request.num) - ); - } else { - result.forEach(element => { - call.write(element); - }); - } - call.end(); - }, - - // This method returns the request - bidiStreamMethod: (call: ServerDuplexStream) => { - call.on('data', (data: TestRequestResponse) => { - if (data.num <= MAX_ERROR_STATUS) { - call.emit( - 'error', - getError('Server Stream Method Error', data.num) - ); - } else { - call.write(data); - } - }); - call.on('end', () => { - call.end(); - }); - }, - }); - const bindAwait = promisify(server.bindAsync); - await bindAwait.call( - server, - 'localhost:' + grpcPort, - grpc.ServerCredentials.createInsecure() as grpcJs.ServerCredentials - ); - server.start(); - return server; - } - - function createClient(grpc: typeof grpcJs | typeof grpcNapi, proto: any) { - return new proto.GrpcTester( - 'localhost:' + grpcPort, - grpc.credentials.createInsecure() - ); - } - - return describe('GrpcPlugin', () => { - let contextManager: ContextManager; - - before(() => { - propagation.setGlobalPropagator(new HttpTraceContext()); - }); - - beforeEach(() => { - contextManager = new AsyncHooksContextManager().enable(); - context.setGlobalContextManager(contextManager); - }); - - afterEach(() => { - context.disable(); - }); - - it('moduleName should be grpc', () => { - assert.deepStrictEqual(moduleName, plugin.moduleName); - }); - - describe('should patch client constructor makeClientConstructor() and makeGenericClientConstructor()', () => { - after(() => { - plugin.disable(); - }); - - it('should patch client constructor makeClientConstructor() and makeGenericClientConstructor()', () => { - plugin.enable(grpc, new NoopTracerProvider()); - (plugin['_moduleExports'] as any).makeGenericClientConstructor({}); - assert.ok( - plugin['_moduleExports'].makeGenericClientConstructor.__wrapped - ); - }); - }); - - const requestList: TestRequestResponse[] = [{ num: 100 }, { num: 50 }]; - const resultSum = { - num: requestList.reduce((sum, x) => { - return sum + x.num; - }, 0), - }; - const methodList: TestGrpcCall[] = [ - { - description: 'unary call', - methodName: 'UnaryMethod', - method: grpcClient.unaryMethod, - request: requestList[0], - result: requestList[0], - }, - { - description: 'Unary call', - methodName: 'UnaryMethod', - method: grpcClient.UnaryMethod, - request: requestList[0], - result: requestList[0], - }, - { - description: 'camelCase unary call', - methodName: 'camelCaseMethod', - method: grpcClient.camelCaseMethod, - request: requestList[0], - result: requestList[0], - }, - { - description: 'clientStream call', - methodName: 'ClientStreamMethod', - method: grpcClient.clientStreamMethod, - request: requestList, - result: resultSum, - }, - { - description: 'serverStream call', - methodName: 'ServerStreamMethod', - method: grpcClient.serverStreamMethod, - request: resultSum, - result: replicate(resultSum), - }, - { - description: 'bidiStream call', - methodName: 'BidiStreamMethod', - method: grpcClient.bidiStreamMethod, - request: requestList, - result: requestList, - }, - ]; - - const runTest = ( - method: typeof methodList[0], - provider: NodeTracerProvider, - checkSpans = true - ) => { - it(`should ${ - checkSpans ? 'do' : 'not' - }: create a rootSpan for client and a childSpan for server - ${ - method.description - }`, async () => { - const args = [client, method.request, method.metadata]; - await (method.method as any) - .apply({}, args) - .then((result: TestRequestResponse | TestRequestResponse[]) => { - assert.ok( - checkEqual(result)(method.result), - 'gRPC call returns correct values' - ); - const spans = memoryExporter.getFinishedSpans(); - if (checkSpans) { - const incomingSpan = spans[0]; - const outgoingSpan = spans[1]; - const validations = { - name: `grpc.pkg_test.GrpcTester/${method.methodName}`, - status: grpc.status.OK, - }; - - assert.strictEqual(spans.length, 2); - assertSpan( - moduleName, - incomingSpan, - SpanKind.SERVER, - validations - ); - assertSpan( - moduleName, - outgoingSpan, - SpanKind.CLIENT, - validations - ); - assertPropagation(incomingSpan, outgoingSpan); - } else { - assert.strictEqual(spans.length, 0); - } - }); - }); - - it(`should raise an error for client childSpan/server rootSpan - ${method.description} - status = OK`, () => { - const expectEmpty = memoryExporter.getFinishedSpans(); - assert.strictEqual(expectEmpty.length, 0); - - const span = provider - .getTracer('default') - .startSpan('TestSpan', { kind: SpanKind.PRODUCER }); - return context.with(setSpan(context.active(), span), async () => { - const rootSpan = getSpan(context.active()); - if (!rootSpan) { - return assert.ok(false); - } - assert.deepStrictEqual(rootSpan, span); - - const args = [client, method.request, method.metadata]; - await (method.method as any) - .apply({}, args) - .then(() => { - // Assert - if (checkSpans) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 2); - const serverSpan = spans[0]; - const clientSpan = spans[1]; - const validations = { - name: `grpc.pkg_test.GrpcTester/${method.methodName}`, - status: grpc.status.OK, - }; - assertSpan( - moduleName, - serverSpan, - SpanKind.SERVER, - validations - ); - assertSpan( - moduleName, - clientSpan, - SpanKind.CLIENT, - validations - ); - assertPropagation(serverSpan, clientSpan); - assert.strictEqual( - rootSpan.context().traceId, - serverSpan.spanContext.traceId - ); - assert.strictEqual( - rootSpan.context().spanId, - clientSpan.parentSpanId - ); - } - }) - .catch((err: ServiceError) => { - assert.ok(false, err); - }); - }); - }); - }; - - const insertError = ( - request: TestRequestResponse | TestRequestResponse[] - ) => (code: number) => - request instanceof Array ? [{ num: code }, ...request] : { num: code }; - - const runErrorTest = ( - method: typeof methodList[0], - key: string, - errorCode: number, - provider: NodeTracerProvider - ) => { - it(`should raise an error for client/server rootSpans: method=${method.methodName}, status=${key}`, async () => { - const expectEmpty = memoryExporter.getFinishedSpans(); - assert.strictEqual(expectEmpty.length, 0); - - const args = [client, insertError(method.request)(errorCode)]; - - await (method.method as any) - .apply({}, args) - .then(() => { - assert.ok(false); - }) - .catch((err: ServiceError) => { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 2, 'Expect 2 ended spans'); - - const validations = { - name: `grpc.pkg_test.GrpcTester/${method.methodName}`, - status: errorCode, - }; - const serverRoot = spans[0]; - const clientRoot = spans[1]; - assertSpan(moduleName, serverRoot, SpanKind.SERVER, validations); - assertSpan(moduleName, clientRoot, SpanKind.CLIENT, validations); - assertPropagation(serverRoot, clientRoot); - }); - }); - - it(`should raise an error for client childSpan/server rootSpan - ${method.description} - status = ${key}`, () => { - const expectEmpty = memoryExporter.getFinishedSpans(); - assert.strictEqual(expectEmpty.length, 0); - - const span = provider - .getTracer('default') - .startSpan('TestSpan', { kind: SpanKind.PRODUCER }); - return context.with(setSpan(context.active(), span), async () => { - const rootSpan = getSpan(context.active()); - if (!rootSpan) { - return assert.ok(false); - } - assert.deepStrictEqual(rootSpan, span); - - const args = [client, insertError(method.request)(errorCode)]; - - await (method.method as any) - .apply({}, args) - .then(() => { - assert.ok(false); - }) - .catch((err: ServiceError) => { - // Assert - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 2); - const serverSpan = spans[0]; - const clientSpan = spans[1]; - const validations = { - name: `grpc.pkg_test.GrpcTester/${method.methodName}`, - status: errorCode, - }; - assertSpan(moduleName, serverSpan, SpanKind.SERVER, validations); - assertSpan(moduleName, clientSpan, SpanKind.CLIENT, validations); - assertPropagation(serverSpan, clientSpan); - assert.strictEqual( - rootSpan.context().traceId, - serverSpan.spanContext.traceId - ); - assert.strictEqual( - rootSpan.context().spanId, - clientSpan.parentSpanId - ); - }); - }); - }); - }; - - describe('enable()', () => { - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(async () => { - const config = { - // TODO: add plugin options here once supported - }; - const patchedGrpc = plugin.enable(grpc, provider, config); - - const packageDefinition = await protoLoader.load(PROTO_PATH, options); - const proto = patchedGrpc.loadPackageDefinition(packageDefinition) - .pkg_test; - - server = await startServer(patchedGrpc, proto); - client = createClient(patchedGrpc, proto); - }); - - after(done => { - client.close(); - server.tryShutdown(() => { - plugin.disable(); - done(); - }); - }); - - methodList.forEach(method => { - describe(`Test automatic tracing for grpc remote method ${method.description}`, () => { - runTest(method, provider); - }); - }); - - methodList.forEach(method => { - describe(`Test error raising for grpc remote ${method.description}`, () => { - Object.keys(grpc.status).forEach((statusKey: string) => { - const errorCode = Number(grpc.status[statusKey as any]); - if (errorCode > grpc.status.OK) { - runErrorTest(method, statusKey, errorCode, provider); - } - }); - }); - }); - }); - - describe('disable()', () => { - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(async () => { - plugin.enable(grpc, provider); - plugin.disable(); - - const packageDefinition = await protoLoader.load(PROTO_PATH, options); - const proto = grpc.loadPackageDefinition(packageDefinition).pkg_test; - - server = await startServer(grpc, proto); - client = createClient(grpc, proto); - }); - - after(done => { - client.close(); - server.tryShutdown(() => { - done(); - }); - }); - - methodList.map(method => { - describe(`Test automatic tracing for grpc remote method ${method.description}`, () => { - runTest(method, provider, false); - }); - }); - }); - - describe('Test filtering requests using metadata', () => { - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(async () => { - const config = { - // TODO: add plugin options here once supported - }; - const patchedGrpc = plugin.enable(grpc, provider, config); - - const packageDefinition = await protoLoader.load(PROTO_PATH, options); - const proto = patchedGrpc.loadPackageDefinition(packageDefinition) - .pkg_test; - - server = await startServer(patchedGrpc, proto); - client = createClient(patchedGrpc, proto); - }); - - after(done => { - client.close(); - server.tryShutdown(() => { - plugin.disable(); - done(); - }); - }); - }); - - describe('Test filtering requests using options', () => { - const provider = new NodeTracerProvider(); - const checkSpans: { [key: string]: boolean } = { - unaryMethod: false, - UnaryMethod: false, - camelCaseMethod: false, - ClientStreamMethod: true, - ServerStreamMethod: true, - BidiStreamMethod: false, - }; - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(async () => { - const config = { - ignoreGrpcMethods: [ - 'UnaryMethod', - new RegExp(/^camel.*Method$/), - (str: string) => str === 'BidiStreamMethod', - ], - }; - const patchedGrpc = plugin.enable( - grpc, - provider, - config as PluginConfig - ); - - const packageDefinition = await protoLoader.load(PROTO_PATH, options); - const proto = patchedGrpc.loadPackageDefinition(packageDefinition) - .pkg_test; - - server = await startServer(patchedGrpc, proto); - client = createClient(patchedGrpc, proto); - }); - - after(done => { - client.close(); - server.tryShutdown(() => { - plugin.disable(); - done(); - }); - }); - - methodList.map(method => { - describe(`Test should ${ - checkSpans[method.methodName] ? '' : 'not ' - }create spans for grpc remote method ${method.methodName}`, () => { - runTest(method, provider, checkSpans[method.methodName]); - }); - }); - }); - }); -}; diff --git a/packages/opentelemetry-grpc-utils/test/index.ts b/packages/opentelemetry-grpc-utils/test/index.ts deleted file mode 100644 index 169fad3841..0000000000 --- a/packages/opentelemetry-grpc-utils/test/index.ts +++ /dev/null @@ -1,16 +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. - */ -export * from './grpcUtils.test'; diff --git a/packages/opentelemetry-grpc-utils/test/utils/assertionUtils.ts b/packages/opentelemetry-grpc-utils/test/utils/assertionUtils.ts deleted file mode 100644 index b9ef2b41d5..0000000000 --- a/packages/opentelemetry-grpc-utils/test/utils/assertionUtils.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanKind, SpanStatusCode } from '@opentelemetry/api'; -import * as assert from 'assert'; -import type * as grpc from 'grpc'; -import type * as grpcJs from '@grpc/grpc-js'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { - hrTimeToMilliseconds, - hrTimeToMicroseconds, -} from '@opentelemetry/core'; - -export const grpcStatusCodeToOpenTelemetryStatusCode = ( - status: grpc.status | grpcJs.status -): SpanStatusCode => { - if (status !== undefined && status === 0) { - return SpanStatusCode.OK; - } - return SpanStatusCode.ERROR; -}; - -export const assertSpan = ( - component: string, - span: ReadableSpan, - kind: SpanKind, - validations: { name: string; status: grpc.status | grpcJs.status } -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - - assert.ok(span.endTime); - assert.strictEqual(span.links.length, 0); - - assert.ok( - hrTimeToMicroseconds(span.startTime) < hrTimeToMicroseconds(span.endTime) - ); - assert.ok(hrTimeToMilliseconds(span.endTime) > 0); - - if (span.kind === SpanKind.SERVER) { - assert.ok(span.spanContext); - } - - // validations - assert.strictEqual(span.name, validations.name); - assert.strictEqual( - span.status.code, - grpcStatusCodeToOpenTelemetryStatusCode(validations.status) - ); -}; - -// Check if sourceSpan was propagated to targetSpan -export const assertPropagation = ( - incomingSpan: ReadableSpan, - outgoingSpan: ReadableSpan -) => { - const targetSpanContext = incomingSpan.spanContext; - const sourceSpanContext = outgoingSpan.spanContext; - assert.strictEqual(targetSpanContext.traceId, sourceSpanContext.traceId); - assert.strictEqual(incomingSpan.parentSpanId, sourceSpanContext.spanId); - assert.strictEqual( - targetSpanContext.traceFlags, - sourceSpanContext.traceFlags - ); - assert.notStrictEqual(targetSpanContext.spanId, sourceSpanContext.spanId); -}; diff --git a/packages/opentelemetry-grpc-utils/tsconfig.json b/packages/opentelemetry-grpc-utils/tsconfig.json deleted file mode 100644 index a6187d76f2..0000000000 --- a/packages/opentelemetry-grpc-utils/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "build" - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "references": [ - { - "path": "../opentelemetry-context-async-hooks" - }, - { - "path": "../opentelemetry-core" - }, - { - "path": "../opentelemetry-node" - }, - { - "path": "../opentelemetry-semantic-conventions" - }, - { - "path": "../opentelemetry-tracing" - } - ] -} diff --git a/packages/opentelemetry-instrumentation-fetch/README.md b/packages/opentelemetry-instrumentation-fetch/README.md index 13bfec30e4..2e139bd5eb 100644 --- a/packages/opentelemetry-instrumentation-fetch/README.md +++ b/packages/opentelemetry-instrumentation-fetch/README.md @@ -21,12 +21,9 @@ import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing import { WebTracerProvider } from '@opentelemetry/web'; import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'; import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; -const provider = new WebTracerProvider({ - plugins: [ - new FetchInstrumentation(), - ], -}); +const provider = new WebTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); @@ -34,6 +31,11 @@ provider.register({ contextManager: new ZoneContextManager(), }); +registerInstrumentations({ + instrumentations: [new FetchInstrumentation()], + tracerProvider: provider, +}); + // or plugin can be also initialised separately and then set the tracer provider or meter provider const fetchInstrumentation = new FetchInstrumentation(); const provider = new WebTracerProvider(); @@ -59,6 +61,14 @@ fetch('http://localhost:8090/fetch.js'); See [examples/tracer-web/fetch](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web) for a short example. +### Fetch Instrumentation options + +Fetch instrumentation plugin has few options available to choose from. You can set the following: + +| Options | Type | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------- | +| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-instrumentation-fetch/src/fetch.ts#L47) | `HttpCustomAttributeFunction` | Function for adding custom attributes | + ## Useful links - For more information on OpenTelemetry, visit: diff --git a/packages/opentelemetry-instrumentation-fetch/package.json b/packages/opentelemetry-instrumentation-fetch/package.json index 6dc48ab77a..dfe5619a7a 100644 --- a/packages/opentelemetry-instrumentation-fetch/package.json +++ b/packages/opentelemetry-instrumentation-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-fetch", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry fetch automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -42,12 +42,13 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", - "@opentelemetry/context-zone": "^0.18.0", - "@opentelemetry/propagator-b3": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-zone": "0.18.2", + "@opentelemetry/propagator-b3": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/shimmer": "1.0.1", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", @@ -60,24 +61,26 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "@opentelemetry/web": "^0.18.0" + "@opentelemetry/core": "0.18.2", + "@opentelemetry/instrumentation": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2", + "@opentelemetry/web": "0.18.2" } } diff --git a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts index 7b9c935694..c3ce12fad7 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts @@ -19,4 +19,6 @@ */ export enum AttributeNames { COMPONENT = 'component', + HTTP_ERROR_NAME = 'http.error_name', + HTTP_STATUS_TEXT = 'http.status_text', } diff --git a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index b263c11015..8d28649794 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -19,11 +19,12 @@ import { isWrapped, InstrumentationBase, InstrumentationConfig, + safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation'; import * as core from '@opentelemetry/core'; import * as web from '@opentelemetry/web'; import { AttributeNames } from './enums/AttributeNames'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { FetchError, FetchResponse, SpanData } from './types'; import { VERSION } from './version'; @@ -43,6 +44,14 @@ const getUrlNormalizingAnchor = () => { return a; }; +export interface FetchCustomAttributeFunction { + ( + span: api.Span, + request: Request | RequestInit, + result: Response | FetchError + ): void; +} + /** * FetchPlugin Config */ @@ -61,6 +70,8 @@ export interface FetchInstrumentationConfig extends InstrumentationConfig { * also not be traced. */ ignoreUrls?: Array; + /** Function for adding custom attributes on the span */ + applyCustomAttributesOnSpan?: FetchCustomAttributeFunction; } /** @@ -121,16 +132,16 @@ export class FetchInstrumentation extends InstrumentationBase< response: FetchResponse ): void { const parsedUrl = web.parseUrl(response.url); - span.setAttribute(HttpAttribute.HTTP_STATUS_CODE, response.status); + span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status); if (response.statusText != null) { - span.setAttribute(HttpAttribute.HTTP_STATUS_TEXT, response.statusText); + span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText); } - span.setAttribute(HttpAttribute.HTTP_HOST, parsedUrl.host); + span.setAttribute(SemanticAttributes.HTTP_HOST, parsedUrl.host); span.setAttribute( - HttpAttribute.HTTP_SCHEME, + SemanticAttributes.HTTP_SCHEME, parsedUrl.protocol.replace(':', '') ); - span.setAttribute(HttpAttribute.HTTP_USER_AGENT, navigator.userAgent); + span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent); } /** @@ -145,6 +156,11 @@ export class FetchInstrumentation extends InstrumentationBase< this._getConfig().propagateTraceHeaderCorsUrls ) ) { + const headers: Partial> = {}; + api.propagation.inject(api.context.active(), headers); + if (Object.keys(headers).length > 0) { + api.diag.debug('headers inject skipped due to CORS policy'); + } return; } @@ -191,8 +207,8 @@ export class FetchInstrumentation extends InstrumentationBase< kind: api.SpanKind.CLIENT, attributes: { [AttributeNames.COMPONENT]: this.moduleName, - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_URL]: url, + [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_URL]: url, }, }); } @@ -292,8 +308,8 @@ export class FetchInstrumentation extends InstrumentationBase< ): Promise { const url = input instanceof Request ? input.url : input; const options = input instanceof Request ? input : init || {}; - const span = plugin._createSpan(url, options); - if (!span) { + const createdSpan = plugin._createSpan(url, options); + if (!createdSpan) { return original.apply(this, [url, options]); } const spanData = plugin._prepareSpanData(url); @@ -306,6 +322,7 @@ export class FetchInstrumentation extends InstrumentationBase< response: Response ) { try { + plugin._applyAttributesAfterFetch(span, options, response); if (response.status >= 200 && response.status < 400) { plugin._endSpan(span, spanData, response); } else { @@ -326,6 +343,7 @@ export class FetchInstrumentation extends InstrumentationBase< error: FetchError ) { try { + plugin._applyAttributesAfterFetch(span, options, error); plugin._endSpan(span, spanData, { status: error.status || 0, statusText: error.message, @@ -338,15 +356,15 @@ export class FetchInstrumentation extends InstrumentationBase< return new Promise((resolve, reject) => { return api.context.with( - api.setSpan(api.context.active(), span), + api.setSpan(api.context.active(), createdSpan), () => { plugin._addHeaders(options, url); plugin._tasksCount++; return original .apply(this, [url, options]) .then( - (onSuccess as any).bind(this, span, resolve), - onError.bind(this, span, reject) + (onSuccess as any).bind(this, createdSpan, resolve), + onError.bind(this, createdSpan, reject) ); } ); @@ -355,6 +373,28 @@ export class FetchInstrumentation extends InstrumentationBase< }; } + private _applyAttributesAfterFetch( + span: api.Span, + request: Request | RequestInit, + result: Response | FetchError + ) { + const applyCustomAttributesOnSpan = this._getConfig() + .applyCustomAttributesOnSpan; + if (applyCustomAttributesOnSpan) { + safeExecuteInTheMiddle( + () => applyCustomAttributesOnSpan(span, request, result), + error => { + if (!error) { + return; + } + + api.diag.error('applyCustomAttributesOnSpan', error); + }, + true + ); + } + } + /** * Prepares a span data - needed later for matching appropriate network * resources diff --git a/packages/opentelemetry-instrumentation-fetch/src/version.ts b/packages/opentelemetry-instrumentation-fetch/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/version.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 9212774ca1..a9568f71cc 100644 --- a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -35,9 +35,13 @@ import { } from '@opentelemetry/web'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { FetchInstrumentation, FetchInstrumentationConfig } from '../src'; +import { + FetchInstrumentation, + FetchInstrumentationConfig, + FetchCustomAttributeFunction, +} from '../src'; import { AttributeNames } from '../src/enums/AttributeNames'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -57,6 +61,7 @@ const getData = (url: string, method?: string) => }, }); +const CUSTOM_ATTRIBUTE_KEY = 'span kind'; const defaultResource = { connectEnd: 15, connectStart: 13, @@ -335,37 +340,37 @@ describe('fetch', () => { assert.strictEqual( attributes[keys[1]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[2]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.ok( attributes[keys[4]] === 'OK' || attributes[keys[4]] === '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.ok( (attributes[keys[5]] as string).indexOf('localhost') === 0, - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.ok( (attributes[keys[8]] as number) > 0, - `attributes ${HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` + `attributes ${SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` ); assert.strictEqual(keys.length, 9, 'number of attributes is wrong'); @@ -537,10 +542,19 @@ describe('fetch', () => { }); describe('when propagateTraceHeaderCorsUrls does NOT MATCH', () => { + let spyDebug: sinon.SinonSpy; beforeEach(done => { + const diagLogger = new api.DiagConsoleLogger(); + spyDebug = sinon.spy(); + diagLogger.debug = spyDebug; + api.diag.setLogger(diagLogger, api.DiagLogLevel.ALL); clearData(); prepareData(done, url, {}); }); + afterEach(() => { + sinon.restore(); + }); + it('should NOT set trace headers', () => { assert.strictEqual( lastResponse.headers[X_B3_TRACE_ID], @@ -558,6 +572,79 @@ describe('fetch', () => { `trace header '${X_B3_SAMPLED}' should not be set` ); }); + it('should debug info that injecting headers was skipped', () => { + assert.strictEqual( + spyDebug.lastCall.args[0], + 'headers inject skipped due to CORS policy' + ); + }); + }); + }); + + describe('applyCustomAttributesOnSpan option', () => { + const noop = () => {}; + const prepare = ( + url: string, + applyCustomAttributesOnSpan: FetchCustomAttributeFunction, + cb: VoidFunction = noop + ) => { + const propagateTraceHeaderCorsUrls = [url]; + + prepareData(cb, url, { + propagateTraceHeaderCorsUrls, + applyCustomAttributesOnSpan, + }); + }; + + afterEach(() => { + clearData(); + }); + + it('applies attributes when the request is succesful', done => { + prepare( + url, + span => { + span.setAttribute(CUSTOM_ATTRIBUTE_KEY, 'custom value'); + }, + () => { + const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; + const attributes = span.attributes; + + assert.ok(attributes[CUSTOM_ATTRIBUTE_KEY] === 'custom value'); + done(); + } + ); + }); + + it('applies custom attributes when the request fails', done => { + prepare( + badUrl, + span => { + span.setAttribute(CUSTOM_ATTRIBUTE_KEY, 'custom value'); + }, + () => { + const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; + const attributes = span.attributes; + + assert.ok(attributes[CUSTOM_ATTRIBUTE_KEY] === 'custom value'); + done(); + } + ); + }); + + it('has request and response objects in callback arguments', done => { + const applyCustomAttributes: FetchCustomAttributeFunction = ( + span, + request, + response + ) => { + assert.ok(request.method === 'GET'); + assert.ok(response.status === 200); + + done(); + }; + + prepare(url, applyCustomAttributes); }); }); @@ -749,7 +836,7 @@ describe('fetch', () => { assert.strictEqual( attributes[keys[3]], 200, - `Missing basic attribute ${HttpAttribute.HTTP_STATUS_CODE}` + `Missing basic attribute ${SemanticAttributes.HTTP_STATUS_CODE}` ); }); }); diff --git a/packages/opentelemetry-instrumentation-grpc/README.md b/packages/opentelemetry-instrumentation-grpc/README.md index a340cb692d..1556863e70 100644 --- a/packages/opentelemetry-instrumentation-grpc/README.md +++ b/packages/opentelemetry-instrumentation-grpc/README.md @@ -25,21 +25,18 @@ To load a specific instrumentation (**gRPC** in this case), specify it in the No ```javascript const { NodeTracerProvider } = require('@opentelemetry/node'); const { GrpcInstrumentation } = require('@opentelemetry/instrumentation-grpc'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const provider = new NodeTracerProvider({ - // be sure to disable old plugin - plugins: { - grpc: { enabled: false, path: '@opentelemetry/plugin-groc' } - }, -}); - -const grpcInstrumentation = new GrpcInstrumentation({ - // see under for available configuration -}); -grpcInstrumentation.enable(); +const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); provider.register(); + +registerInstrumentations({ + instrumentations: [new GrpcInstrumentation()] + tracerProvider: provider, +}); + ``` See [examples/grpc](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc) or [examples/grpc-js](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc-js) for examples. diff --git a/packages/opentelemetry-instrumentation-grpc/package.json b/packages/opentelemetry-instrumentation-grpc/package.json index cebdd88ccc..4bca3ce7e4 100644 --- a/packages/opentelemetry-instrumentation-grpc/package.json +++ b/packages/opentelemetry-instrumentation-grpc/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-grpc", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry grpc automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -42,13 +42,14 @@ }, "devDependencies": { "@grpc/grpc-js": "1.2.12", - "@grpc/proto-loader": "0.5.6", - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@grpc/proto-loader": "0.6.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-async-hooks": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/node": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "@types/shimmer": "1.0.1", "@types/sinon": "9.0.11", @@ -60,15 +61,17 @@ "nyc": "15.1.0", "rimraf": "3.0.2", "semver": "7.3.5", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0" + "@opentelemetry/api-metrics": "0.18.2", + "@opentelemetry/instrumentation": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2" } } diff --git a/packages/opentelemetry-instrumentation-grpc/src/enums.ts b/packages/opentelemetry-instrumentation-grpc/src/enums.ts new file mode 100644 index 0000000000..9dfd3e0923 --- /dev/null +++ b/packages/opentelemetry-instrumentation-grpc/src/enums.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + GRPC_KIND = 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD = 'grpc.method', + GRPC_ERROR_NAME = 'grpc.error_name', + GRPC_ERROR_MESSAGE = 'grpc.error_message', +} diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts index 645149c051..a53daa3ee0 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts @@ -24,7 +24,6 @@ import { propagation, context, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import { _grpcStatusCodeToSpanStatus, @@ -33,6 +32,8 @@ import { } from '../utils'; import { CALL_SPAN_ENDED } from './serverUtils'; import { EventEmitter } from 'events'; +import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Parse a package method list and return a list of methods to patch @@ -89,16 +90,19 @@ export function makeGrpcClientRemoteCall( if (err) { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.UNSET.toString() ); } @@ -124,8 +128,8 @@ export function makeGrpcClientRemoteCall( } span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -154,8 +158,8 @@ export function makeGrpcClientRemoteCall( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index cfb83288bb..bec965b22e 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -42,7 +42,6 @@ import { setSpan, diag, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; import { shouldNotTraceServerCall, handleServerFunction, @@ -54,6 +53,7 @@ import { getMetadata, } from './clientUtils'; import { EventEmitter } from 'events'; +import { AttributeNames } from '../enums'; export class GrpcJsInstrumentation extends InstrumentationBase { constructor( @@ -197,7 +197,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts index eac6238350..ab953af8c6 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts @@ -21,7 +21,6 @@ */ import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import type { ServerCallWithMeta, @@ -34,6 +33,8 @@ import { _methodIsIgnored, } from '../utils'; import { IgnoreMatcher } from '../types'; +import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); @@ -70,7 +71,7 @@ function serverStreamAndBidiHandler( code: SpanStatusCode.UNSET, }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); @@ -90,8 +91,8 @@ function serverStreamAndBidiHandler( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -121,16 +122,19 @@ function clientStreamAndUnaryHandler( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts index fda7b4c7ed..503ab2c136 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts @@ -17,7 +17,7 @@ import type * as grpcTypes from 'grpc'; import type * as events from 'events'; import { SendUnaryDataCallback, GrpcClientFunc } from './types'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { context, Span, @@ -31,6 +31,7 @@ import { _grpcStatusCodeToOpenTelemetryStatusCode, findIndex, } from '../utils'; +import { AttributeNames } from '../enums'; /** * This method handles the client remote call @@ -55,16 +56,19 @@ export const makeGrpcClientRemoteCall = function ( if (err) { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -96,8 +100,8 @@ export const makeGrpcClientRemoteCall = function ( span.addEvent('sent'); span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -123,8 +127,8 @@ export const makeGrpcClientRemoteCall = function ( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); } @@ -135,7 +139,7 @@ export const makeGrpcClientRemoteCall = function ( (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, status.code.toString() ); endSpan(); diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 0fe0d30bab..25bf00bb2c 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -29,7 +29,6 @@ import { GrpcClientFunc, } from './types'; import { GrpcInstrumentationConfig } from '../types'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; import { context, propagation, @@ -45,6 +44,7 @@ import { } from './serverUtils'; import { makeGrpcClientRemoteCall, getMetadata } from './clientUtils'; import { _methodIsIgnored } from '../utils'; +import { AttributeNames } from '../enums'; /** * Holding reference to grpc module here to access constant of grpc modules @@ -205,7 +205,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts index cd928a4665..9d6673c09f 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts @@ -17,13 +17,14 @@ import type * as grpcTypes from 'grpc'; import { SendUnaryDataCallback, ServerCallWithMeta } from './types'; import { GrpcNativeInstrumentation } from './'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; import { context, Span, SpanStatusCode } from '@opentelemetry/api'; import { _grpcStatusCodeToOpenTelemetryStatusCode, _grpcStatusCodeToSpanStatus, _methodIsIgnored, } from '../utils'; +import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const clientStreamAndUnaryHandler = function ( grpcClient: typeof grpcTypes, @@ -47,16 +48,19 @@ export const clientStreamAndUnaryHandler = function ( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -89,7 +93,7 @@ export const serverStreamAndBidiHandler = function ( call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, call.status.code.toString() ); @@ -107,8 +111,8 @@ export const serverStreamAndBidiHandler = function ( }); span.addEvent('finished with error'); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); diff --git a/packages/opentelemetry-instrumentation-grpc/src/version.ts b/packages/opentelemetry-instrumentation-grpc/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/version.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-instrumentation-http/README.md b/packages/opentelemetry-instrumentation-http/README.md index d0757bd476..0755af4b63 100644 --- a/packages/opentelemetry-instrumentation-http/README.md +++ b/packages/opentelemetry-instrumentation-http/README.md @@ -24,25 +24,20 @@ To load a specific instrumentation (HTTP in this case), specify it in the Node T ```js const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); - const { ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); const { NodeTracerProvider } = require('@opentelemetry/node'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const provider = new NodeTracerProvider({ - // be sure to disable old plugins - plugins: { - http: { enabled: false, path: '@opentelemetry/plugin-http' }, - https: { enabled: false, path: '@opentelemetry/plugin-https' } - }, -}); - -const httpInstrumentation = new HttpInstrumentation({ - // see under for available configuration -}); -httpInstrumentation.enable(); +const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); provider.register(); + +registerInstrumentations({ + instrumentations: [new HttpInstrumentation()], + tracerProvider: provider, +}); + ``` See [examples/http](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/http) for a short example. diff --git a/packages/opentelemetry-instrumentation-http/package.json b/packages/opentelemetry-instrumentation-http/package.json index 04f1376f73..6f27af5d99 100644 --- a/packages/opentelemetry-instrumentation-http/package.json +++ b/packages/opentelemetry-instrumentation-http/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-http", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry http/https automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -41,13 +41,14 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-async-hooks": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/node": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "@types/got": "9.6.11", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/request-promise-native": "1.0.17", "@types/semver": "7.3.4", "@types/sinon": "9.0.11", @@ -62,16 +63,18 @@ "request": "2.88.2", "request-promise-native": "1.0.9", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "superagent": "6.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", + "@opentelemetry/instrumentation": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2", "semver": "^7.1.3" } } diff --git a/packages/opentelemetry-instrumentation-http/src/enums.ts b/packages/opentelemetry-instrumentation-http/src/enums.ts new file mode 100644 index 0000000000..f9b8be3c8e --- /dev/null +++ b/packages/opentelemetry-instrumentation-http/src/enums.ts @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_ERROR_NAME = 'http.error_name', + HTTP_ERROR_MESSAGE = 'http.error_message', + HTTP_STATUS_TEXT = 'http.status_text', +} diff --git a/packages/opentelemetry-instrumentation-http/src/http.ts b/packages/opentelemetry-instrumentation-http/src/http.ts index 69d581c78a..59d9539c33 100644 --- a/packages/opentelemetry-instrumentation-http/src/http.ts +++ b/packages/opentelemetry-instrumentation-http/src/http.ts @@ -291,7 +291,12 @@ export class HttpInstrumentation extends InstrumentationBase { this._callRequestHook(span, request); } - request.on( + /* + * User 'response' event listeners can be added before our listener, + * force our listener to be the first, so response emitter is bound + * before any user listeners are added to it. + */ + request.prependListener( 'response', (response: http.IncomingMessage & { aborted?: boolean }) => { const responseAttributes = utils.getOutgoingRequestAttributesOnResponse( @@ -400,71 +405,35 @@ export class HttpInstrumentation extends InstrumentationBase { }), }; - return context.with(propagation.extract(ROOT_CONTEXT, headers), () => { - const span = instrumentation._startHttpSpan( - `${component.toLocaleUpperCase()} ${method}`, - spanOptions - ); - - return context.with(setSpan(context.active(), span), () => { - context.bind(request); - context.bind(response); - - if (instrumentation._getConfig().requestHook) { - instrumentation._callRequestHook(span, request); - } - if (instrumentation._getConfig().responseHook) { - instrumentation._callResponseHook(span, response); - } - - // Wraps end (inspired by: - // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/instrumentations/instrumentation-connect.ts#L75) - const originalEnd = response.end; - response.end = function ( - this: http.ServerResponse, - ..._args: ResponseEndArgs - ) { - response.end = originalEnd; - // Cannot pass args of type ResponseEndArgs, - const returned = safeExecuteInTheMiddle( - () => response.end.apply(this, arguments as any), - error => { - if (error) { - utils.setSpanWithError(span, error); - instrumentation._closeHttpSpan(span); - throw error; - } - } - ); - - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); + const ctx = propagation.extract(ROOT_CONTEXT, headers); + const span = instrumentation._startHttpSpan( + `${component.toLocaleUpperCase()} ${method}`, + spanOptions, + ctx + ); - span - .setAttributes(attributes) - .setStatus(utils.parseResponseStatus(response.statusCode)); - - if (instrumentation._getConfig().applyCustomAttributesOnSpan) { - safeExecuteInTheMiddle( - () => - instrumentation._getConfig().applyCustomAttributesOnSpan!( - span, - request, - response - ), - () => {}, - true - ); - } + return context.with(setSpan(ctx, span), () => { + context.bind(request); + context.bind(response); - instrumentation._closeHttpSpan(span); - return returned; - }; + if (instrumentation._getConfig().requestHook) { + instrumentation._callRequestHook(span, request); + } + if (instrumentation._getConfig().responseHook) { + instrumentation._callResponseHook(span, response); + } - return safeExecuteInTheMiddle( - () => original.apply(this, [event, ...args]), + // Wraps end (inspired by: + // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/instrumentations/instrumentation-connect.ts#L75) + const originalEnd = response.end; + response.end = function ( + this: http.ServerResponse, + ..._args: ResponseEndArgs + ) { + response.end = originalEnd; + // Cannot pass args of type ResponseEndArgs, + const returned = safeExecuteInTheMiddle( + () => response.end.apply(this, arguments as never), error => { if (error) { utils.setSpanWithError(span, error); @@ -473,7 +442,43 @@ export class HttpInstrumentation extends InstrumentationBase { } } ); - }); + + const attributes = utils.getIncomingRequestAttributesOnResponse( + request, + response + ); + + span + .setAttributes(attributes) + .setStatus(utils.parseResponseStatus(response.statusCode)); + + if (instrumentation._getConfig().applyCustomAttributesOnSpan) { + safeExecuteInTheMiddle( + () => + instrumentation._getConfig().applyCustomAttributesOnSpan!( + span, + request, + response + ), + () => {}, + true + ); + } + + instrumentation._closeHttpSpan(span); + return returned; + }; + + return safeExecuteInTheMiddle( + () => original.apply(this, [event, ...args]), + error => { + if (error) { + utils.setSpanWithError(span, error); + instrumentation._closeHttpSpan(span); + throw error; + } + } + ); }); }; } @@ -528,37 +533,53 @@ export class HttpInstrumentation extends InstrumentationBase { kind: SpanKind.CLIENT, }; const span = instrumentation._startHttpSpan(operationName, spanOptions); + + const parentContext = context.active(); + const requestContext = setSpan(parentContext, span); + if (!optionsParsed.headers) { optionsParsed.headers = {}; } - propagation.inject( - setSpan(context.active(), span), - optionsParsed.headers - ); + propagation.inject(requestContext, optionsParsed.headers); + + return context.with(requestContext, () => { + /* + * The response callback is registered before ClientRequest is bound, + * thus it is needed to bind it before the function call. + */ + const cb = args[args.length - 1]; + if (typeof cb === 'function') { + args[args.length - 1] = context.bind(cb, parentContext); + } - const request: http.ClientRequest = safeExecuteInTheMiddle( - () => original.apply(this, [optionsParsed, ...args]), - error => { - if (error) { - utils.setSpanWithError(span, error); - instrumentation._closeHttpSpan(span); - throw error; + const request: http.ClientRequest = safeExecuteInTheMiddle( + () => original.apply(this, [optionsParsed, ...args]), + error => { + if (error) { + utils.setSpanWithError(span, error); + instrumentation._closeHttpSpan(span); + throw error; + } } - } - ); + ); - diag.debug('%s instrumentation outgoingRequest', component); - context.bind(request); - return instrumentation._traceClientRequest( - component, - request, - optionsParsed, - span - ); + diag.debug('%s instrumentation outgoingRequest', component); + context.bind(request, parentContext); + return instrumentation._traceClientRequest( + component, + request, + optionsParsed, + span + ); + }); }; } - private _startHttpSpan(name: string, options: SpanOptions) { + private _startHttpSpan( + name: string, + options: SpanOptions, + ctx = context.active() + ) { /* * If a parent is required but not present, we use a `NoopSpan` to still * propagate context without recording it. @@ -569,16 +590,16 @@ export class HttpInstrumentation extends InstrumentationBase { : this._getConfig().requireParentforIncomingSpans; let span: Span; - const currentSpan = getSpan(context.active()); + const currentSpan = getSpan(ctx); if (requireParent === true && currentSpan === undefined) { // TODO: Refactor this when a solution is found in // https://github.com/open-telemetry/opentelemetry-specification/issues/530 - span = NOOP_TRACER.startSpan(name, options); + span = NOOP_TRACER.startSpan(name, options, ctx); } else if (requireParent === true && currentSpan?.context().isRemote) { span = currentSpan; } else { - span = this.tracer.startSpan(name, options); + span = this.tracer.startSpan(name, options, ctx); } this._spanNotEnded.add(span); return span; diff --git a/packages/opentelemetry-instrumentation-http/src/types.ts b/packages/opentelemetry-instrumentation-http/src/types.ts index 94cfb52f60..a9927b35d7 100644 --- a/packages/opentelemetry-instrumentation-http/src/types.ts +++ b/packages/opentelemetry-instrumentation-http/src/types.ts @@ -44,7 +44,7 @@ export type ParsedRequestOptions = | http.RequestOptions; export type Http = typeof http; export type Https = typeof https; -/* tslint:disable-next-line:no-any */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type Func = (...args: any[]) => T; export type ResponseEndArgs = | [((() => void) | undefined)?] diff --git a/packages/opentelemetry-instrumentation-http/src/utils.ts b/packages/opentelemetry-instrumentation-http/src/utils.ts index d0ac02caba..4802ecca21 100644 --- a/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -20,8 +20,8 @@ import { SpanStatus, } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttributes, } from '@opentelemetry/semantic-conventions'; import { ClientRequest, @@ -33,6 +33,7 @@ import { } from 'http'; import { Socket } from 'net'; import * as url from 'url'; +import { AttributeNames } from './enums'; import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; /** @@ -158,8 +159,8 @@ export const setSpanWithError = ( const message = error.message; span.setAttributes({ - [HttpAttribute.HTTP_ERROR_NAME]: error.name, - [HttpAttribute.HTTP_ERROR_MESSAGE]: message, + [AttributeNames.HTTP_ERROR_NAME]: error.name, + [AttributeNames.HTTP_ERROR_MESSAGE]: message, }); if (!obj) { @@ -194,9 +195,11 @@ export const setRequestContentLengthAttribute = ( if (length === null) return; if (isCompressed(request.headers)) { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH] = length; } else { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED] = length; + attributes[ + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + ] = length; } }; @@ -213,10 +216,10 @@ export const setResponseContentLengthAttribute = ( if (length === null) return; if (isCompressed(response.headers)) { - attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH] = length; } else { attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -341,18 +344,18 @@ export const getOutgoingRequestAttributes = ( const headers = requestOptions.headers || {}; const userAgent = headers['user-agent']; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestOptions, headers, `${options.component}:` ), - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_TARGET]: requestOptions.path || '/', - [GeneralAttribute.NET_PEER_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_TARGET]: requestOptions.path || '/', + [SemanticAttributes.NET_PEER_NAME]: hostname, }; if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } return attributes; }; @@ -364,11 +367,11 @@ export const getOutgoingRequestAttributes = ( export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { - attributes[HttpAttribute.HTTP_FLAVOR] = kind; + attributes[SemanticAttributes.HTTP_FLAVOR] = kind; if (kind.toUpperCase() !== 'QUIC') { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_TCP; + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_UDP; + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; } } return attributes; @@ -386,15 +389,15 @@ export const getOutgoingRequestAttributesOnResponse = ( const { statusCode, statusMessage, httpVersion, socket } = response; const { remoteAddress, remotePort } = socket; const attributes: SpanAttributes = { - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; setResponseContentLengthAttribute(response, attributes); if (statusCode) { - attributes[HttpAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[HttpAttribute.HTTP_STATUS_TEXT] = ( + attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; + attributes[AttributeNames.HTTP_STATUS_TEXT] = ( statusMessage || '' ).toUpperCase(); } @@ -425,31 +428,31 @@ export const getIncomingRequestAttributes = ( 'localhost'; const serverName = options.serverName; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestUrl, headers, `${options.component}:` ), - [HttpAttribute.HTTP_HOST]: host, - [GeneralAttribute.NET_HOST_NAME]: hostname, - [HttpAttribute.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_HOST]: host, + [SemanticAttributes.NET_HOST_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, }; if (typeof ips === 'string') { - attributes[HttpAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; + attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(',')[0]; } if (typeof serverName === 'string') { - attributes[HttpAttribute.HTTP_SERVER_NAME] = serverName; + attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[HttpAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[HttpAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; } if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } setRequestContentLengthAttribute(request, attributes); @@ -483,16 +486,16 @@ export const getIncomingRequestAttributesOnResponse = ( : undefined; const attributes: SpanAttributes = { - [GeneralAttribute.NET_HOST_IP]: localAddress, - [GeneralAttribute.NET_HOST_PORT]: localPort, - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_STATUS_CODE]: statusCode, - [HttpAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), + [SemanticAttributes.NET_HOST_IP]: localAddress, + [SemanticAttributes.NET_HOST_PORT]: localPort, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, + [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), }; if (route !== undefined) { - attributes[HttpAttribute.HTTP_ROUTE] = route; + attributes[SemanticAttributes.HTTP_ROUTE] = route; } return attributes; }; diff --git a/packages/opentelemetry-instrumentation-http/src/version.ts b/packages/opentelemetry-instrumentation-http/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-instrumentation-http/src/version.ts +++ b/packages/opentelemetry-instrumentation-http/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index 8f75058b3b..2361b26756 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -28,8 +28,8 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttributes, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as nock from 'nock'; @@ -172,15 +172,16 @@ describe('HttpInstrumentation', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); }); + describe('with good instrumentation options', () => { beforeEach(() => { memoryExporter.reset(); @@ -248,25 +249,28 @@ describe('HttpInstrumentation', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); [ { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttributes.HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + span.attributes[SemanticAttributes.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -657,7 +661,7 @@ describe('HttpInstrumentation', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); @@ -705,7 +709,15 @@ describe('HttpInstrumentation', () => { assert.deepStrictEqual(getSpan(context.active()), undefined); http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { assert.deepStrictEqual(getSpan(context.active()), undefined); - done(); + + res.on('data', () => { + assert.deepStrictEqual(getSpan(context.active()), undefined); + }); + + res.on('end', () => { + assert.deepStrictEqual(getSpan(context.active()), undefined); + done(); + }); }); }); }); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index 86de0fbefa..fe473f408f 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -30,8 +30,8 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - GeneralAttribute, - HttpAttribute, + NetTransportValues, + SemanticAttributes, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as fs from 'fs'; @@ -160,11 +160,11 @@ describe('HttpsInstrumentation', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); @@ -240,15 +240,15 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); @@ -256,10 +256,13 @@ describe('HttpsInstrumentation', () => { { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttributes.HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + span.attributes[SemanticAttributes.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -658,7 +661,7 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index 26bef9061c..2e5fdcce13 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -21,7 +21,7 @@ import { TraceFlags, } from '@opentelemetry/api'; import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import { IncomingMessage, ServerResponse } from 'http'; @@ -30,6 +30,7 @@ import * as sinon from 'sinon'; import * as url from 'url'; import { IgnoreMatcher } from '../../src/types'; import * as utils from '../../src/utils'; +import { AttributeNames } from '../../src/enums'; describe('Utility', () => { describe('parseResponseStatus()', () => { @@ -258,10 +259,10 @@ describe('Utility', () => { utils.setSpanWithError(span, new Error(errorMessage), obj as any); const attributes = span.attributes; assert.strictEqual( - attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + attributes[AttributeNames.HTTP_ERROR_MESSAGE], errorMessage ); - assert.ok(attributes[HttpAttribute.HTTP_ERROR_NAME]); + assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]); } }); }); @@ -293,7 +294,7 @@ describe('Utility', () => { const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], '/test/toto'); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], '/test/toto'); }); it('should succesfully process without middleware stack', () => { @@ -303,7 +304,7 @@ describe('Utility', () => { const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], undefined); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); }); // Verify the key in the given attributes is set to the given value, @@ -313,14 +314,14 @@ describe('Utility', () => { key: string | undefined, value: number ) { - const httpAttributes = [ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + const SemanticAttributess = [ + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, ]; - for (const attr of httpAttributes) { + for (const attr of SemanticAttributess) { if (attr === key) { assert.strictEqual(attributes[attr], value); } else { @@ -341,7 +342,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -357,7 +358,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -373,7 +374,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 1200 ); }); @@ -392,7 +393,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -411,7 +412,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -430,7 +431,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 1200 ); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts index 9995cd985c..75d21dd731 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts @@ -16,8 +16,9 @@ import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttributes, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as url from 'url'; @@ -229,10 +230,13 @@ describe('HttpInstrumentation Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTP GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttributes.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttributes.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts index 7df9f3e771..729aa1fe91 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts @@ -16,8 +16,9 @@ import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttributes, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; @@ -238,10 +239,13 @@ describe('HttpsInstrumentation Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTPS GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttributes.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttributes.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index 97e67f7557..ff0229c135 100644 --- a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -16,14 +16,12 @@ import { SpanKind, SpanStatus } from '@opentelemetry/api'; import { hrTimeToNanoseconds } from '@opentelemetry/core'; import { ReadableSpan } from '@opentelemetry/tracing'; -import { - GeneralAttribute, - HttpAttribute, -} from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import * as utils from '../../src/utils'; import { DummyPropagation } from './DummyPropagation'; +import { AttributeNames } from '../../src/enums'; export const assertSpan = ( span: ReadableSpan, @@ -49,19 +47,19 @@ export const assertSpan = ( `${validations.component.toUpperCase()} ${validations.httpMethod}` ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], + span.attributes[SemanticAttributes.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], + span.attributes[SemanticAttributes.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], validations.httpStatusCode ); @@ -81,7 +79,7 @@ export const assertSpan = ( const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttributes.HTTP_USER_AGENT], userAgent ); } @@ -95,34 +93,34 @@ export const assertSpan = ( validations.resHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); } } assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], + span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, 'must be consistent (PEER_NAME and hostname)' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], + span.attributes[SemanticAttributes.NET_PEER_IP], 'must have PEER_IP' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], + span.attributes[SemanticAttributes.NET_PEER_PORT], 'must have PEER_PORT' ); assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, 'must be consistent' ); @@ -136,13 +134,13 @@ export const assertSpan = ( validations.reqHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -150,16 +148,16 @@ export const assertSpan = ( } if (validations.serverName) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, ' must have serverName attribute' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], + span.attributes[SemanticAttributes.NET_HOST_PORT], 'must have HOST_PORT' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], + span.attributes[SemanticAttributes.NET_HOST_IP], 'must have HOST_IP' ); } diff --git a/packages/opentelemetry-instrumentation-xml-http-request/README.md b/packages/opentelemetry-instrumentation-xml-http-request/README.md index 1883d1fbb5..c9333a04aa 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/README.md +++ b/packages/opentelemetry-instrumentation-xml-http-request/README.md @@ -20,19 +20,27 @@ import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing import { WebTracerProvider } from '@opentelemetry/web'; import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; + +const providerWithZone = new WebTracerProvider(); +providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); -// this is still possible -const providerWithZone = new WebTracerProvider({ - plugins: [ - new XMLHttpRequestInstrumentation({ - propagateTraceHeaderCorsUrls: ['http://localhost:8090'] - }) - ] -}); providerWithZone.register({ contextManager: new ZoneContextManager(), }); + +registerInstrumentations({ + instrumentations: [ + new XMLHttpRequestInstrumentation({ + propagateTraceHeaderCorsUrls: ['http://localhost:8090'] + }), + ], + tracerProvider: provider, +}); + + const webTracerWithZone = providerWithZone.getTracer('default'); + ///////////////////////////////////////// // or plugin can be also initialised separately and then set the tracer provider or meter provider @@ -46,7 +54,6 @@ providerWithZone.register({ xmlHttpRequestInstrumentation.setTracerProvider(providerWithZone); ///////////////////////////////////////// -providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); // and some test const req = new XMLHttpRequest(); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/package.json b/packages/opentelemetry-instrumentation-xml-http-request/package.json index 09c7c707e5..9fef4fb17b 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/package.json +++ b/packages/opentelemetry-instrumentation-xml-http-request/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation-xml-http-request", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry XMLHttpRequest automatic instrumentation package.", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -42,12 +42,13 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", - "@opentelemetry/context-zone": "^0.18.0", - "@opentelemetry/propagator-b3": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-zone": "0.18.2", + "@opentelemetry/propagator-b3": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "babel-loader": "8.2.2", @@ -59,24 +60,26 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "@opentelemetry/web": "^0.18.0" + "@opentelemetry/core": "0.18.2", + "@opentelemetry/instrumentation": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2", + "@opentelemetry/web": "0.18.2" } } diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts new file mode 100644 index 0000000000..dd1fce77be --- /dev/null +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_STATUS_TEXT = 'http.status_text', +} diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/version.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/version.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts index 84c0b18c83..ab9f4f4fa9 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts @@ -21,7 +21,7 @@ import { InstrumentationConfig, } from '@opentelemetry/instrumentation'; import { hrTime, isUrlIgnored, otperformance } from '@opentelemetry/core'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addSpanNetworkEvents, getResource, @@ -37,6 +37,7 @@ import { XhrMem, } from './types'; import { VERSION } from './version'; +import { AttributeNames } from './enums/AttributeNames'; // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer @@ -106,6 +107,11 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase> = {}; + api.propagation.inject(api.context.active(), headers); + if (Object.keys(headers).length > 0) { + api.diag.debug('headers inject skipped due to CORS policy'); + } return; } const headers: { [key: string]: unknown } = {}; @@ -145,20 +151,23 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { // content length comes from the PerformanceTiming resource; this ensures that our // matching logic found the right one assert.ok( - (span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] as any) > - 0 + (span.attributes[ + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH + ] as any) > 0 ); done(); }, 500); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts index 689e299aa8..d9818bb9c0 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts @@ -25,7 +25,7 @@ import { } from '@opentelemetry/propagator-b3'; import { ZoneContextManager } from '@opentelemetry/context-zone'; import * as tracing from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { PerformanceTimingNames as PTN, WebTracerProvider, @@ -35,6 +35,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { EventNames } from '../src/enums/EventNames'; import { XMLHttpRequestInstrumentation } from '../src/xhr'; +import { AttributeNames } from '../src/enums/AttributeNames'; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -289,39 +290,39 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.ok( (attributes[keys[2]] as number) > 0, - 'attributes ${HttpAttributes.HTTP_RESPONSE_CONTENT_SIZE} <= 0' + 'attributes ${SemanticAttributess.HTTP_RESPONSE_CONTENT_SIZE} <= 0' ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], 'OK', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], parseUrl(url).host, - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); @@ -549,7 +550,12 @@ describe('xhr', () => { 'AND origin does NOT match window.location And does NOT match' + ' with propagateTraceHeaderCorsUrls', () => { + let spyDebug: sinon.SinonSpy; beforeEach(done => { + const diagLogger = new api.DiagConsoleLogger(); + spyDebug = sinon.spy(); + diagLogger.debug = spyDebug; + api.diag.setLogger(diagLogger, api.DiagLogLevel.ALL); clearData(); prepareData( done, @@ -573,6 +579,13 @@ describe('xhr', () => { `trace header '${X_B3_SAMPLED}' should not be set` ); }); + + it('should debug info that injecting headers was skipped', () => { + assert.strictEqual( + spyDebug.lastCall.args[0], + 'headers inject skipped due to CORS policy' + ); + }); } ); @@ -681,7 +694,7 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[1]], secondUrl, - `attribute ${HttpAttribute.HTTP_URL} is wrong` + `attribute ${SemanticAttributes.HTTP_URL} is wrong` ); }); }); @@ -777,40 +790,40 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH} is wrong` + `attributes ${SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH} is wrong` ); assert.strictEqual( attributes[keys[3]], 400, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], 'Bad Request', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); @@ -912,35 +925,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); @@ -1004,35 +1017,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); @@ -1098,35 +1111,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); diff --git a/packages/opentelemetry-instrumentation/README.md b/packages/opentelemetry-instrumentation/README.md index 802440fa79..55dcad32db 100644 --- a/packages/opentelemetry-instrumentation/README.md +++ b/packages/opentelemetry-instrumentation/README.md @@ -162,7 +162,7 @@ myPLugin.enable(); Successor of loading plugins through TracerProvider "plugins" option. It also supersedes PluginLoader for node. The old configurations usually looks like -### NODE - old way using TracerProvider +### NODE - old way using TracerProvider - not available anymore ```javascript const { NodeTracerProvider } = require('@opentelemetry/node'); @@ -177,7 +177,7 @@ provider.register({ }); ``` -### WEB - old way using TracerProvider +### WEB - old way using TracerProvider - not available anymore ```javascript const { WebTracerProvider } = require('@opentelemetry/web'); @@ -208,28 +208,22 @@ All plugins will be bound to TracerProvider as well as instrumentations ```javascript const { B3Propagator } = require('@opentelemetry/propagator-b3'); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const { NodeTracerProvider } = require('@opentelemetry/node'); + const tracerProvider = new NodeTracerProvider(); +tracerProvider.register({ + propagator: new B3Propagator(), +}); + registerInstrumentations({ instrumentations: [ - new UserInteractionPlugin(), - new XMLHttpRequestInstrumentation({ - ignoreUrls: [/localhost/], - propagateTraceHeaderCorsUrls: [ - 'http://localhost:8090', - ], - }), + new HttpInstrumentation(), ], - meterProvider: meterProvider, tracerProvider: tracerProvider, }); -tracerProvider.register({ - propagator: new B3Propagator(), -}); - ``` ### WEB - Auto Loader @@ -237,28 +231,26 @@ tracerProvider.register({ ```javascript const { B3Propagator } = require('@opentelemetry/propagator-b3'); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; -const { UserInteractionPlugin } = require('@opentelemetry/plugin-user-interaction'); +const { XMLHttpRequestInstrumentation } = require('@opentelemetry/instrumentation-xml-http-request'); const { WebTracerProvider } = require('@opentelemetry/web'); + const tracerProvider = new WebTracerProvider(); +tracerProvider.register({ + propagator: new B3Propagator(), +}); + registerInstrumentations({ instrumentations: [ - new GraphQLInstrumentation(), - { - plugins: { - http: { enabled: false }, - }, - } + new XMLHttpRequestInstrumentation({ + ignoreUrls: [/localhost/], + propagateTraceHeaderCorsUrls: [ + 'http://localhost:8090', + ], + }), ], - meterProvider: meterProvider, tracerProvider: tracerProvider, }); - -tracerProvider.register({ - propagator: new B3Propagator(), -}); - ``` ## License diff --git a/packages/opentelemetry-instrumentation/package.json b/packages/opentelemetry-instrumentation/package.json index 45e0c9757e..383495fd6d 100644 --- a/packages/opentelemetry-instrumentation/package.json +++ b/packages/opentelemetry-instrumentation/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/instrumentation", - "version": "0.18.0", + "version": "0.18.2", "description": "Base class for node which OpenTelemetry instrumentation modules extend", "author": "OpenTelemetry Authors", "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", @@ -54,16 +54,19 @@ "url": "https://github.com/open-telemetry/opentelemetry-js/issues" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", + "@opentelemetry/api-metrics": "0.18.2", "require-in-the-middle": "^5.0.3", "semver": "^7.3.2", "shimmer": "^1.2.1" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "devDependencies": { - "@babel/core": "7.13.10", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "@types/shimmer": "1.0.1", "@types/sinon": "9.0.11", @@ -78,17 +81,17 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" } } diff --git a/packages/opentelemetry-instrumentation/src/autoLoader.ts b/packages/opentelemetry-instrumentation/src/autoLoader.ts index 795b1f83ce..862ba111e0 100644 --- a/packages/opentelemetry-instrumentation/src/autoLoader.ts +++ b/packages/opentelemetry-instrumentation/src/autoLoader.ts @@ -21,7 +21,6 @@ import { enableInstrumentations, parseInstrumentationOptions, } from './autoLoaderUtils'; -import { loadOldPlugins } from './platform'; import { AutoLoaderOptions } from './types_internal'; /** @@ -33,20 +32,15 @@ import { AutoLoaderOptions } from './types_internal'; export function registerInstrumentations( options: AutoLoaderOptions ): () => void { - const { - instrumentations, - pluginsNode, - pluginsWeb, - } = parseInstrumentationOptions(options.instrumentations); + const { instrumentations } = parseInstrumentationOptions( + options.instrumentations + ); const tracerProvider = options.tracerProvider || trace.getTracerProvider(); const meterProvider = options.meterProvider || metrics.getMeterProvider(); enableInstrumentations(instrumentations, tracerProvider, meterProvider); - const unload = loadOldPlugins(pluginsNode, pluginsWeb, tracerProvider); - return () => { - unload(); disableInstrumentations(instrumentations); }; } diff --git a/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts index c92d4c9ffe..e070fb3d10 100644 --- a/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts +++ b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts @@ -18,11 +18,6 @@ import { TracerProvider } from '@opentelemetry/api'; import { MeterProvider } from '@opentelemetry/api-metrics'; import { Instrumentation } from './types'; import { AutoLoaderResult, InstrumentationOption } from './types_internal'; -import { - NodePlugins, - NodePluginsTracerConfiguration, - OldClassPlugin, -} from './types_plugin_only'; /** * Parses the options and returns instrumentations, node plugins and @@ -33,31 +28,19 @@ export function parseInstrumentationOptions( options: InstrumentationOption[] = [] ): AutoLoaderResult { let instrumentations: Instrumentation[] = []; - let pluginsNode: NodePlugins = {}; - let pluginsWeb: OldClassPlugin[] = []; for (let i = 0, j = options.length; i < j; i++) { const option = options[i] as any; if (Array.isArray(option)) { const results = parseInstrumentationOptions(option); instrumentations = instrumentations.concat(results.instrumentations); - pluginsWeb = pluginsWeb.concat(results.pluginsWeb); - pluginsNode = Object.assign({}, pluginsNode, results.pluginsNode); - } else if ((option as NodePluginsTracerConfiguration).plugins) { - pluginsNode = Object.assign( - {}, - pluginsNode, - (option as NodePluginsTracerConfiguration).plugins - ); } else if (typeof option === 'function') { instrumentations.push(new option()); } else if ((option as Instrumentation).instrumentationName) { instrumentations.push(option); - } else if ((option as OldClassPlugin).moduleName) { - pluginsWeb.push(option as OldClassPlugin); } } - return { instrumentations, pluginsNode, pluginsWeb }; + return { instrumentations }; } /** diff --git a/packages/opentelemetry-instrumentation/src/platform/browser/index.ts b/packages/opentelemetry-instrumentation/src/platform/browser/index.ts index fc42da7384..24c76056a1 100644 --- a/packages/opentelemetry-instrumentation/src/platform/browser/index.ts +++ b/packages/opentelemetry-instrumentation/src/platform/browser/index.ts @@ -15,4 +15,3 @@ */ export * from './instrumentation'; -export * from './old/autoLoader'; diff --git a/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts deleted file mode 100644 index c6aedb836f..0000000000 --- a/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts +++ /dev/null @@ -1,42 +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. - */ - -// This should be removed after plugins are gone - -import { TracerProvider } from '@opentelemetry/api'; -import { NodePlugins, OldClassPlugin } from '../../../types_plugin_only'; - -/** - * Loads provided web plugins - * @param pluginsNode - * @param pluginsWeb - * @param tracerProvider - * @return returns function to disable all plugins - */ -export function loadOldPlugins( - pluginsNode: NodePlugins, - pluginsWeb: OldClassPlugin[], - tracerProvider: TracerProvider -): () => void { - pluginsWeb.forEach(plugin => { - plugin.enable([], tracerProvider); - }); - return () => { - pluginsWeb.forEach(plugin => { - plugin.disable(); - }); - }; -} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/index.ts b/packages/opentelemetry-instrumentation/src/platform/node/index.ts index 9f5254d653..842797c341 100644 --- a/packages/opentelemetry-instrumentation/src/platform/node/index.ts +++ b/packages/opentelemetry-instrumentation/src/platform/node/index.ts @@ -16,5 +16,4 @@ export * from './instrumentation'; export * from './instrumentationNodeModuleDefinition'; export * from './instrumentationNodeModuleFile'; -export * from './old/autoLoader'; export * from './types'; diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts deleted file mode 100644 index abd84513c3..0000000000 --- a/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts +++ /dev/null @@ -1,224 +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. - */ - -// This is copy from previous version, should be removed after plugins are gone - -import { TracerProvider, diag } from '@opentelemetry/api'; -import * as RequireInTheMiddle from 'require-in-the-middle'; -import { OldClassPlugin, OldPluginConfig } from '../../../types_plugin_only'; -import * as utils from './utils'; - -// States for the Plugin Loader -export enum HookState { - UNINITIALIZED, - ENABLED, - DISABLED, -} - -/** - * Environment variable which will contain list of modules to not load corresponding plugins for - * e.g.OTEL_NO_PATCH_MODULES=pg,https,mongodb - */ -export const ENV_PLUGIN_DISABLED_LIST = 'OTEL_NO_PATCH_MODULES'; - -/** - * Wildcard symbol. If ignore list is set to this, disable all plugins - */ -const DISABLE_ALL_PLUGINS = '*'; - -export interface Plugins { - [pluginName: string]: OldPluginConfig; -} - -/** - * Returns the Plugins object that meet the below conditions. - * Valid criteria: 1. It should be enabled. 2. Should have non-empty path. - */ -function filterPlugins(plugins: Plugins): Plugins { - const keys = Object.keys(plugins); - return keys.reduce((acc: Plugins, key: string) => { - if (plugins[key].enabled && (plugins[key].path || plugins[key].plugin)) { - acc[key] = plugins[key]; - } - return acc; - }, {}); -} - -/** - * Parse process.env[ENV_PLUGIN_DISABLED_LIST] for a list of modules - * not to load corresponding plugins for. - */ -function getIgnoreList(): string[] | typeof DISABLE_ALL_PLUGINS { - const envIgnoreList: string = process.env[ENV_PLUGIN_DISABLED_LIST] || ''; - if (envIgnoreList === DISABLE_ALL_PLUGINS) { - return envIgnoreList; - } - return envIgnoreList.split(',').map(v => v.trim()); -} - -/** - * The PluginLoader class can load instrumentation plugins that use a patch - * mechanism to enable automatic tracing for specific target modules. - */ -export class PluginLoader { - /** A list of loaded plugins. */ - plugins: OldClassPlugin[] = []; - /** - * A field that tracks whether the require-in-the-middle hook has been loaded - * for the first time, as well as whether the hook body is activated or not. - */ - private _hookState = HookState.UNINITIALIZED; - - /** Constructs a new PluginLoader instance. */ - constructor(readonly provider: TracerProvider) {} - - /** - * Loads a list of plugins. Each plugin module should implement the core - * {@link Plugin} interface and export an instance named as 'plugin'. This - * function will attach a hook to be called the first time the module is - * loaded. - * @param Plugins an object whose keys are plugin names and whose - * {@link OldPluginConfig} values indicate several configuration options. - */ - load(plugins: Plugins): PluginLoader { - if (this._hookState === HookState.UNINITIALIZED) { - const pluginsToLoad = filterPlugins(plugins); - const modulesToHook = Object.keys(pluginsToLoad); - const modulesToIgnore = getIgnoreList(); - // Do not hook require when no module is provided. In this case it is - // not necessary. With skipping this step we lower our footprint in - // customer applications and require-in-the-middle won't show up in CPU - // frames. - if (modulesToHook.length === 0) { - this._hookState = HookState.DISABLED; - return this; - } - - const requiredModulesToHook = modulesToHook.filter((name: string) => { - try { - const moduleResolvedFilename = require.resolve(name); - return moduleResolvedFilename in require.cache; - } catch { - return false; - } - }); - if (requiredModulesToHook.length > 0) { - diag.warn( - `Some modules (${requiredModulesToHook.join( - ', ' - )}) were already required when their respective plugin was loaded, some plugins might not work. Make sure the SDK is setup before you require in other modules.` - ); - } - - // Enable the require hook. - RequireInTheMiddle(modulesToHook, (exports, name, baseDir) => { - if (this._hookState !== HookState.ENABLED) return exports; - const config = pluginsToLoad[name]; - const modulePath = config.path!; - const modulePlugin = config.plugin; - let version = null; - - if (!baseDir) { - // basedir is the directory where the module is located, - // or undefined for core modules. - // lets plugins restrict what they support for core modules (see plugin.supportedVersions) - version = process.versions.node; - } else { - // Get the module version. - version = utils.getPackageVersion(baseDir); - } - - // Skip loading of all modules if '*' is provided - if (modulesToIgnore === DISABLE_ALL_PLUGINS) { - diag.info( - `PluginLoader#load: skipped patching module ${name} because all plugins are disabled (${ENV_PLUGIN_DISABLED_LIST})` - ); - return exports; - } - - if (modulesToIgnore.includes(name)) { - diag.info( - `PluginLoader#load: skipped patching module ${name} because it was on the ignore list (${ENV_PLUGIN_DISABLED_LIST})` - ); - return exports; - } - - diag.info(`PluginLoader#load: trying to load ${name}@${version}`); - - if (!version) return exports; - - diag.debug( - `PluginLoader#load: applying patch to ${name}@${version} using ${modulePath} module` - ); - - // Expecting a plugin from module; - try { - const plugin: OldClassPlugin = - modulePlugin ?? require(modulePath).plugin; - if (!utils.isSupportedVersion(version, plugin.supportedVersions)) { - diag.warn( - `PluginLoader#load: Plugin ${name} only supports module ${plugin.moduleName} with the versions: ${plugin.supportedVersions}` - ); - return exports; - } - if (plugin.moduleName !== name) { - diag.error( - `PluginLoader#load: Entry ${name} use a plugin that instruments ${plugin.moduleName}` - ); - return exports; - } - - this.plugins.push(plugin); - // Enable each supported plugin. - return plugin.enable(exports, this.provider, config); - } catch (e) { - diag.error( - `PluginLoader#load: could not load plugin ${modulePath} of module ${name}. Error: ${e.message}` - ); - return exports; - } - }); - this._hookState = HookState.ENABLED; - } else if (this._hookState === HookState.DISABLED) { - diag.error( - 'PluginLoader#load: Currently cannot re-enable plugin loader.' - ); - } else { - diag.error('PluginLoader#load: Plugin loader already enabled.'); - } - return this; - } - - /** Unloads plugins. */ - unload(): PluginLoader { - if (this._hookState === HookState.ENABLED) { - for (const plugin of this.plugins) { - plugin.disable(); - } - this.plugins = []; - this._hookState = HookState.DISABLED; - } - return this; - } -} - -/** - * Adds a search path for plugin modules. Intended for testing purposes only. - * @param searchPath The path to add. - */ -export function searchPathForTest(searchPath: string) { - module.paths.push(searchPath); -} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts deleted file mode 100644 index 2fa030f0f2..0000000000 --- a/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts +++ /dev/null @@ -1,81 +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. - */ - -// This should be removed after plugins are gone - -import { TracerProvider } from '@opentelemetry/api'; -import { NodePlugins, OldClassPlugin } from '../../../types_plugin_only'; -import { PluginLoader } from './PluginLoader'; - -/** List of all default supported plugins */ -export const DEFAULT_INSTRUMENTATION_PLUGINS: NodePlugins = { - mongodb: { enabled: true, path: '@opentelemetry/plugin-mongodb' }, - grpc: { enabled: true, path: '@opentelemetry/plugin-grpc' }, - '@grpc/grpc-js': { enabled: true, path: '@opentelemetry/plugin-grpc-js' }, - http: { enabled: true, path: '@opentelemetry/plugin-http' }, - https: { enabled: true, path: '@opentelemetry/plugin-https' }, - mysql: { enabled: true, path: '@opentelemetry/plugin-mysql' }, - pg: { enabled: true, path: '@opentelemetry/plugin-pg' }, - redis: { enabled: true, path: '@opentelemetry/plugin-redis' }, - ioredis: { enabled: true, path: '@opentelemetry/plugin-ioredis' }, - 'pg-pool': { enabled: true, path: '@opentelemetry/plugin-pg-pool' }, - express: { enabled: true, path: '@opentelemetry/plugin-express' }, - '@hapi/hapi': { enabled: true, path: '@opentelemetry/hapi-instrumentation' }, - koa: { enabled: true, path: '@opentelemetry/koa-instrumentation' }, - dns: { enabled: true, path: '@opentelemetry/plugin-dns' }, -}; - -/** - * Loads provided node plugins - * @param pluginsNode - * @param pluginsWeb - * @param tracerProvider - * @return returns function to disable all plugins - */ -export function loadOldPlugins( - pluginsNode: NodePlugins, - pluginsWeb: OldClassPlugin[], - tracerProvider: TracerProvider -): () => void { - const allPlugins = mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, pluginsNode); - const pluginLoader = new PluginLoader(tracerProvider); - pluginLoader.load(allPlugins); - return () => { - pluginLoader.unload(); - }; -} - -function mergePlugins( - defaultPlugins: NodePlugins, - userSuppliedPlugins: NodePlugins -): NodePlugins { - const mergedUserSuppliedPlugins: NodePlugins = {}; - - for (const pluginName in userSuppliedPlugins) { - mergedUserSuppliedPlugins[pluginName] = { - // Any user-supplied non-default plugin should be enabled by default - ...(DEFAULT_INSTRUMENTATION_PLUGINS[pluginName] || { enabled: true }), - ...userSuppliedPlugins[pluginName], - }; - } - - const mergedPlugins: NodePlugins = { - ...defaultPlugins, - ...mergedUserSuppliedPlugins, - }; - - return mergedPlugins; -} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts deleted file mode 100644 index 941afdeb12..0000000000 --- a/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts +++ /dev/null @@ -1,72 +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. - */ - -// This is copy from previous version, should be removed after plugins are gone - -import { diag } from '@opentelemetry/api'; -import * as path from 'path'; -import * as semver from 'semver'; - -/** - * Gets the package version. - * @param basedir The base directory. - */ -export function getPackageVersion(basedir: string): string | null { - const pjsonPath = path.join(basedir, 'package.json'); - try { - const version = require(pjsonPath).version; - // Attempt to parse a string as a semantic version, returning either a - // SemVer object or null. - if (!semver.parse(version)) { - diag.error( - `getPackageVersion: [${pjsonPath}|${version}] Version string could not be parsed.` - ); - return null; - } - return version; - } catch (e) { - diag.error( - `getPackageVersion: [${pjsonPath}] An error occurred while retrieving version string. ${e.message}` - ); - return null; - } -} - -/** - * Determines if a version is supported - * @param moduleVersion a version in [semver](https://semver.org/spec/v2.0.0.html) format. - * @param [supportedVersions] a list of supported versions ([semver](https://semver.org/spec/v2.0.0.html) format). - */ -export function isSupportedVersion( - moduleVersion: string, - supportedVersions?: string[] -) { - if (!Array.isArray(supportedVersions) || supportedVersions.length === 0) { - return true; - } - - return supportedVersions.some(supportedVersion => - semver.satisfies(moduleVersion, supportedVersion) - ); -} - -/** - * Adds a search path for plugin modules. Intended for testing purposes only. - * @param searchPath The path to add. - */ -export function searchPathForTest(searchPath: string) { - module.paths.push(searchPath); -} diff --git a/packages/opentelemetry-instrumentation/src/types_internal.ts b/packages/opentelemetry-instrumentation/src/types_internal.ts index 4831ad6537..6383b6cd17 100644 --- a/packages/opentelemetry-instrumentation/src/types_internal.ts +++ b/packages/opentelemetry-instrumentation/src/types_internal.ts @@ -18,25 +18,15 @@ import { TracerProvider } from '@opentelemetry/api'; import { MeterProvider } from '@opentelemetry/api-metrics'; import { InstrumentationBase } from './platform'; import { Instrumentation } from './types'; -import { - NodePlugins, - NodePluginsTracerConfiguration, - OldClassPlugin, -} from './types_plugin_only'; export type InstrumentationOption = | typeof InstrumentationBase | typeof InstrumentationBase[] | Instrumentation - | Instrumentation[] - | NodePluginsTracerConfiguration - | OldClassPlugin - | OldClassPlugin[]; + | Instrumentation[]; export interface AutoLoaderResult { instrumentations: Instrumentation[]; - pluginsNode: NodePlugins; - pluginsWeb: OldClassPlugin[]; } export interface AutoLoaderOptions { diff --git a/packages/opentelemetry-instrumentation/src/types_plugin_only.ts b/packages/opentelemetry-instrumentation/src/types_plugin_only.ts deleted file mode 100644 index b1e1012e3c..0000000000 --- a/packages/opentelemetry-instrumentation/src/types_plugin_only.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TracerProvider } from '@opentelemetry/api'; - -export interface NodePlugins { - [pluginName: string]: OldPluginConfig; -} - -export interface NodePluginsTracerConfiguration { - plugins: NodePlugins; -} - -/** Interface Plugin to apply patch. */ -export interface OldClassPlugin { - /** - * Contains all supported versions. - * All versions must be compatible with [semver](https://semver.org/spec/v2.0.0.html) format. - * If the version is not supported, we won't apply instrumentation patch (see `enable` method). - * If omitted, all versions of the module will be patched. - */ - supportedVersions?: string[]; - - /** - * Name of the module that the plugin instrument. - */ - moduleName: string; - - /** - * Method that enables the instrumentation patch. - * @param moduleExports The value of the `module.exports` property that would - * normally be exposed by the required module. ex: `http`, `https` etc. - * @param TracerProvider a tracer provider. - * @param [config] an object to configure the plugin. - */ - enable( - moduleExports: T, - TracerProvider: TracerProvider, - config?: OldPluginConfig - ): T; - - /** Method to disable the instrumentation */ - disable(): void; -} - -export interface OldPluginConfig { - /** - * Whether to enable the plugin. - * @default true - */ - enabled?: boolean; - - /** - * Path of the trace plugin to load. - * @default '@opentelemetry/plugin-http' in case of http. - */ - path?: string; - - /** - * Plugin to load - * @example import {plugin} from '@opentelemetry/plugin-http' in case of http. - */ - plugin?: OldClassPlugin; - - /** - * Request methods that match any string in ignoreMethods will not be traced. - */ - ignoreMethods?: string[]; - - /** - * URLs that partially match any regex in ignoreUrls will not be traced. - * In addition, URLs that are _exact matches_ of strings in ignoreUrls will - * also not be traced. - */ - ignoreUrls?: Array; - - /** - * List of internal files that need patch and are not exported by - * default. - */ - internalFilesExports?: PluginInternalFiles; - - /** - * If true, additional information about query parameters and - * results will be attached (as `attributes`) to spans representing - * database operations. - */ - enhancedDatabaseReporting?: boolean; -} - -export interface PluginInternalFilesVersion { - [pluginName: string]: string; -} - -/** - * Each key should be the name of the module to trace, and its value - * a mapping of a property name to a internal plugin file name. - */ -export interface PluginInternalFiles { - [versions: string]: PluginInternalFilesVersion; -} diff --git a/packages/opentelemetry-instrumentation/src/version.ts b/packages/opentelemetry-instrumentation/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-instrumentation/src/version.ts +++ b/packages/opentelemetry-instrumentation/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts b/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts deleted file mode 100644 index a470936123..0000000000 --- a/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { NOOP_TRACER_PROVIDER } from '@opentelemetry/api'; -import { NOOP_METER_PROVIDER } from '@opentelemetry/api-metrics'; -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import { registerInstrumentations } from '../../src'; -import { OldClassPlugin } from '../../src/types_plugin_only'; - -class WebPlugin implements OldClassPlugin { - moduleName = 'WebPlugin'; - enable() {} - disable() {} -} - -describe('autoLoader', () => { - let unload: Function | undefined; - afterEach(() => { - sinon.restore(); - if (typeof unload === 'function') { - unload(); - unload = undefined; - } - }); - - describe('registerInstrumentations', () => { - describe('Old Plugins', () => { - let enableSpy: sinon.SinonSpy; - const tracerProvider = NOOP_TRACER_PROVIDER; - const meterProvider = NOOP_METER_PROVIDER; - let webPlugin: WebPlugin; - beforeEach(() => { - webPlugin = new WebPlugin(); - enableSpy = sinon.spy(webPlugin, 'enable'); - unload = registerInstrumentations({ - instrumentations: [webPlugin], - tracerProvider, - meterProvider, - }); - }); - afterEach(() => { - if (typeof unload === 'function') { - unload(); - unload = undefined; - } - }); - - it('should enable a required plugin', () => { - assert.strictEqual(enableSpy.callCount, 1); - }); - - it('should set TracerProvider', () => { - assert.ok(enableSpy.lastCall.args[1] === tracerProvider); - }); - }); - }); -}); diff --git a/packages/opentelemetry-instrumentation/test/browser/index-webpack.ts b/packages/opentelemetry-instrumentation/test/browser/index-webpack.ts index 99100a0f6e..d49f67165c 100644 --- a/packages/opentelemetry-instrumentation/test/browser/index-webpack.ts +++ b/packages/opentelemetry-instrumentation/test/browser/index-webpack.ts @@ -19,5 +19,5 @@ testsContext.keys().forEach(testsContext); const testsContextCommon = require.context('../common', true, /test$/); testsContextCommon.keys().forEach(testsContextCommon); -const srcContext = require.context('.', true, /src$/); -srcContext.keys().forEach(srcContext); +// const srcContext = require.context('.', true, /src$/); +// srcContext.keys().forEach(srcContext); diff --git a/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts b/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts index a4aaaca473..ae2de44502 100644 --- a/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts +++ b/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts @@ -17,8 +17,6 @@ import * as assert from 'assert'; import { InstrumentationBase } from '../../src'; import { parseInstrumentationOptions } from '../../src/autoLoaderUtils'; -import { InstrumentationOption } from '../../src/types_internal'; -import { OldClassPlugin } from '../../src/types_plugin_only'; class FooInstrumentation extends InstrumentationBase { constructor() { @@ -34,14 +32,6 @@ class FooInstrumentation extends InstrumentationBase { disable() {} } -class FooWebPlugin implements OldClassPlugin { - moduleName = 'foo'; - - enable() {} - - disable() {} -} - // const fooInstrumentation = new FooInstrumentation(); describe('autoLoaderUtils', () => { @@ -63,46 +53,5 @@ describe('autoLoaderUtils', () => { const instrumentation = instrumentations[0]; assert.ok(instrumentation instanceof InstrumentationBase); }); - - it('should return node old plugin', () => { - const { pluginsNode } = parseInstrumentationOptions([ - { - plugins: { - http: { enabled: false }, - }, - }, - ]); - assert.strictEqual(Object.keys(pluginsNode).length, 1); - }); - - it('should return web old plugin', () => { - const { pluginsWeb } = parseInstrumentationOptions([new FooWebPlugin()]); - assert.strictEqual(pluginsWeb.length, 1); - }); - - it('should handle mix of plugins and instrumentations', () => { - const nodePlugins = { - plugins: { - http: { enabled: false }, - https: { enabled: false }, - }, - }; - const options: InstrumentationOption[] = []; - - options.push(new FooWebPlugin()); - options.push(nodePlugins); - options.push([new FooInstrumentation(), new FooInstrumentation()]); - options.push([new FooWebPlugin(), new FooWebPlugin()]); - - const { - pluginsWeb, - pluginsNode, - instrumentations, - } = parseInstrumentationOptions(options); - - assert.strictEqual(pluginsWeb.length, 3); - assert.strictEqual(Object.keys(pluginsNode).length, 2); - assert.strictEqual(instrumentations.length, 2); - }); }); }); diff --git a/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts b/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts deleted file mode 100644 index 937f174860..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { TracerProvider } from '@opentelemetry/api'; -import { OldClassPlugin, OldPluginConfig } from '../../src/types_plugin_only'; - -/** This class represent the base to patch plugin. */ -export abstract class BasePlugin implements OldClassPlugin { - abstract readonly moduleName: string; // required for internalFilesExports - protected _moduleExports!: T; - constructor( - protected readonly _tracerName: string, - protected readonly _tracerVersion?: string - ) {} - - enable( - moduleExports: T, - tracerProvider: TracerProvider, - config?: OldPluginConfig - ): T { - this._moduleExports = moduleExports; - return this.patch(); - } - - disable(): void { - this.unpatch(); - } - - protected abstract patch(): T; - protected abstract unpatch(): void; -} diff --git a/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts b/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts deleted file mode 100644 index b9624e8a4f..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { NoopTracerProvider, diag } from '@opentelemetry/api'; -import * as assert from 'assert'; -import * as path from 'path'; -import sinon = require('sinon'); -import { - HookState, - PluginLoader, - Plugins, - searchPathForTest, - ENV_PLUGIN_DISABLED_LIST, -} from '../../src/platform/node/old/PluginLoader'; - -const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); -/* eslint-disable node/no-extraneous-require */ -const simplePlugins: Plugins = { - 'simple-module': { - enabled: true, - path: '@opentelemetry/plugin-simple-module', - ignoreMethods: [], - ignoreUrls: [], - }, -}; - -const httpPlugins: Plugins = { - http: { - enabled: true, - path: '@opentelemetry/plugin-http-module', - ignoreMethods: [], - ignoreUrls: [], - }, -}; - -const disablePlugins: Plugins = { - 'simple-module': { - enabled: false, - path: '@opentelemetry/plugin-simple-module', - }, - nonexistent: { - enabled: false, - path: '@opentelemetry/plugin-nonexistent-module', - }, -}; - -const nonexistentPlugins: Plugins = { - nonexistent: { - enabled: true, - path: '@opentelemetry/plugin-nonexistent-module', - }, -}; - -const missingPathPlugins: Plugins = { - 'simple-module': { - enabled: true, - }, - nonexistent: { - enabled: true, - }, -}; - -const supportedVersionPlugins: Plugins = { - 'supported-module': { - enabled: true, - path: '@opentelemetry/plugin-supported-module', - }, -}; - -const notSupportedVersionPlugins: Plugins = { - 'notsupported-module': { - enabled: true, - path: 'notsupported-module', - }, -}; - -const alreadyRequiredPlugins: Plugins = { - 'already-require-module': { - enabled: true, - path: '@opentelemetry/plugin-supported-module', - }, -}; - -const differentNamePlugins: Plugins = { - 'random-module': { - enabled: true, - path: '@opentelemetry/plugin-http-module', - }, -}; - -describe('PluginLoader', () => { - const provider = new NoopTracerProvider(); - - before(() => { - module.paths.push(INSTALLED_PLUGINS_PATH); - searchPathForTest(INSTALLED_PLUGINS_PATH); - }); - - afterEach(() => { - // clear require cache - Object.keys(require.cache).forEach(key => delete require.cache[key]); - sinon.restore(); - }); - - describe('.state()', () => { - it('returns UNINITIALIZED when first called', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['_hookState'], HookState.UNINITIALIZED); - }); - - it('transitions from UNINITIALIZED to ENABLED', () => { - const pluginLoader = new PluginLoader(provider); - pluginLoader.load(simplePlugins); - assert.strictEqual(pluginLoader['_hookState'], HookState.ENABLED); - pluginLoader.unload(); - }); - - it('transitions from ENABLED to DISABLED', () => { - const pluginLoader = new PluginLoader(provider); - pluginLoader.load(simplePlugins).unload(); - assert.strictEqual(pluginLoader['_hookState'], HookState.DISABLED); - }); - }); - - describe('.load()', () => { - afterEach(() => { - delete process.env[ENV_PLUGIN_DISABLED_LIST]; - }); - - it('sanity check', () => { - // Ensure that module fixtures contain values that we expect. - const simpleModule = require('simple-module'); - const simpleModule001 = require('supported-module'); - const simpleModule100 = require('notsupported-module'); - - assert.strictEqual(simpleModule.name(), 'simple-module'); - assert.strictEqual(simpleModule001.name(), 'supported-module'); - assert.strictEqual(simpleModule100.name(), 'notsupported-module'); - - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule001.value(), 0); - assert.strictEqual(simpleModule100.value(), 0); - - assert.throws(() => require('nonexistent-module')); - }); - - it('should not load a plugin on the ignore list environment variable', () => { - // Set ignore list env var - process.env[ENV_PLUGIN_DISABLED_LIST] = 'simple-module'; - const pluginLoader = new PluginLoader(provider); - pluginLoader.load({ ...simplePlugins, ...supportedVersionPlugins }); - - assert.strictEqual(pluginLoader['plugins'].length, 0); - - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - - const supportedModule = require('supported-module'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(supportedModule.value(), 1); - assert.strictEqual(supportedModule.name(), 'patched-supported-module'); - - pluginLoader.unload(); - }); - - it('should not load plugins on the ignore list environment variable', () => { - // Set ignore list env var - process.env[ENV_PLUGIN_DISABLED_LIST] = 'simple-module,http'; - const pluginLoader = new PluginLoader(provider); - pluginLoader.load({ - ...simplePlugins, - ...supportedVersionPlugins, - ...httpPlugins, - }); - - assert.strictEqual(pluginLoader['plugins'].length, 0); - - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - - const httpModule = require('http'); - assert.ok(httpModule); - assert.strictEqual(pluginLoader['plugins'].length, 0); - - const supportedModule = require('supported-module'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(supportedModule.value(), 1); - assert.strictEqual(supportedModule.name(), 'patched-supported-module'); - - pluginLoader.unload(); - }); - - it('should not load any plugins if ignore list environment variable is set to "*"', () => { - // Set ignore list env var - process.env[ENV_PLUGIN_DISABLED_LIST] = '*'; - const pluginLoader = new PluginLoader(provider); - pluginLoader.load({ - ...simplePlugins, - ...supportedVersionPlugins, - ...httpPlugins, - }); - - assert.strictEqual(pluginLoader['plugins'].length, 0); - - const simpleModule = require('simple-module'); - const httpModule = require('http'); - const supportedModule = require('supported-module'); - - assert.strictEqual( - pluginLoader['plugins'].length, - 0, - 'No plugins were loaded' - ); - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - assert.ok(httpModule); - assert.strictEqual(supportedModule.value(), 0); - assert.strictEqual(supportedModule.name(), 'supported-module'); - - pluginLoader.unload(); - }); - - it('should load a plugin and patch the target modules', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(simplePlugins); - // The hook is only called the first time the module is loaded. - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(simpleModule.value(), 1); - assert.strictEqual(simpleModule.name(), 'patched-simple-module'); - pluginLoader.unload(); - }); - - it('should load a plugin and patch the core module', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(httpPlugins); - // The hook is only called the first time the module is loaded. - const httpModule = require('http'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(httpModule.get(), 'patched'); - pluginLoader.unload(); - }); - // @TODO: simplify this test once we can load module with custom path - it('should not load the plugin when supported versions does not match', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(notSupportedVersionPlugins); - // The hook is only called the first time the module is loaded. - require('notsupported-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.unload(); - }); - // @TODO: simplify this test once we can load module with custom path - it('should load a plugin and patch the target modules when supported versions match', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(supportedVersionPlugins); - // The hook is only called the first time the module is loaded. - const simpleModule = require('supported-module'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(simpleModule.value(), 1); - assert.strictEqual(simpleModule.name(), 'patched-supported-module'); - pluginLoader.unload(); - }); - - it('should not load a plugin when value is false', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(disablePlugins); - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - pluginLoader.unload(); - }); - - it('should not load a plugin when value is true but path is missing', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(missingPathPlugins); - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - assert.strictEqual(simpleModule.value(), 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - pluginLoader.unload(); - }); - - it('should not load a non existing plugin', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(nonexistentPlugins); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.unload(); - }); - - it("doesn't patch modules for which plugins aren't specified", () => { - const pluginLoader = new PluginLoader(provider); - pluginLoader.load({}); - assert.strictEqual(require('simple-module').value(), 0); - pluginLoader.unload(); - }); - - it('should warn when module was already loaded', () => { - const warnStub = sinon.stub(diag, 'warn'); - require('already-require-module'); - const pluginLoader = new PluginLoader(provider); - pluginLoader.load(alreadyRequiredPlugins); - pluginLoader.unload(); - sinon.assert.calledOnce(warnStub); - const message = warnStub.firstCall.args[0]; - assert.ok(message.match(/were already required when/)); - assert.ok(message.match(/(already-require-module)/)); - }); - - it('should not load a plugin that patches a different module that the one configured', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(differentNamePlugins); - require('random-module'); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.unload(); - }); - }); - - describe('.unload()', () => { - it('should unload the plugins and unpatch the target module when unloads', () => { - const pluginLoader = new PluginLoader(provider); - assert.strictEqual(pluginLoader['plugins'].length, 0); - pluginLoader.load(simplePlugins); - // The hook is only called the first time the module is loaded. - const simpleModule = require('simple-module'); - assert.strictEqual(pluginLoader['plugins'].length, 1); - assert.strictEqual(simpleModule.value(), 1); - assert.strictEqual(simpleModule.name(), 'patched-simple-module'); - pluginLoader.unload(); - assert.strictEqual(pluginLoader['plugins'].length, 0); - assert.strictEqual(simpleModule.name(), 'simple-module'); - assert.strictEqual(simpleModule.value(), 0); - }); - }); -}); diff --git a/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts b/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts deleted file mode 100644 index 9a13de5001..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { NOOP_TRACER_PROVIDER } from '@opentelemetry/api'; -import { NOOP_METER_PROVIDER } from '@opentelemetry/api-metrics'; -import * as assert from 'assert'; -import * as path from 'path'; -import * as sinon from 'sinon'; -import { registerInstrumentations } from '../../src'; -import { - Plugins, - searchPathForTest, -} from '../../src/platform/node/old/PluginLoader'; - -const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); - -const httpPlugin: Plugins = { - http: { - enabled: true, - path: '@opentelemetry/plugin-http-module', - ignoreMethods: [], - ignoreUrls: [], - }, -}; - -describe('autoLoader', () => { - let unload: Function | undefined; - before(() => { - module.paths.push(INSTALLED_PLUGINS_PATH); - searchPathForTest(INSTALLED_PLUGINS_PATH); - }); - - afterEach(() => { - sinon.restore(); - Object.keys(require.cache).forEach(key => delete require.cache[key]); - if (typeof unload === 'function') { - unload(); - unload = undefined; - } - }); - - describe('registerInstrumentations', () => { - describe('Old Plugins', () => { - let enableSpy: sinon.SinonSpy; - const tracerProvider = NOOP_TRACER_PROVIDER; - const meterProvider = NOOP_METER_PROVIDER; - beforeEach(() => { - // eslint-disable-next-line node/no-extraneous-require - const simpleModule = require('@opentelemetry/plugin-simple-module') - .plugin; - enableSpy = sinon.spy(simpleModule, 'enable'); - unload = registerInstrumentations({ - instrumentations: [ - { - plugins: { - ...httpPlugin, - 'simple-module': { enabled: true, plugin: simpleModule }, - }, - }, - ], - tracerProvider, - meterProvider, - }); - }); - afterEach(() => { - Object.keys(require.cache).forEach(key => delete require.cache[key]); - if (typeof unload === 'function') { - unload(); - unload = undefined; - } - }); - - it('should enable a required plugin', () => { - // eslint-disable-next-line node/no-extraneous-require - const simpleModule = require('simple-module'); - assert.ok(simpleModule); - assert.strictEqual(enableSpy.callCount, 1); - }); - - it('should set TracerProvider', () => { - // eslint-disable-next-line node/no-extraneous-require - const simpleModule = require('simple-module'); - assert.ok(simpleModule); - assert.ok(enableSpy.lastCall.args[1] === tracerProvider); - }); - }); - }); -}); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js deleted file mode 100644 index 99c16a8af2..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js +++ /dev/null @@ -1,22 +0,0 @@ -Object.defineProperty(exports, "__esModule", { value: true }); -const { BasePlugin } = require('../../../BasePlugin'); -const shimmer = require("shimmer"); - -class HttpModulePlugin extends BasePlugin { - constructor() { - super(); - this.moduleName = 'http'; - } - - patch() { - shimmer.wrap(this._moduleExports, 'get', orig => () => 'patched'); - return this._moduleExports; - } - - unpatch() { - shimmer.unwrap(this._moduleExports, 'get'); - } -} -exports.HttpModulePlugin = HttpModulePlugin; -const plugin = new HttpModulePlugin(); -exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js deleted file mode 100644 index 4847af1405..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js +++ /dev/null @@ -1,5 +0,0 @@ -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./http-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json deleted file mode 100644 index bb40eab67d..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "@opentelemetry/plugin-http-module", - "version": "0.0.1" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js deleted file mode 100644 index 1b22b5ce90..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js +++ /dev/null @@ -1,5 +0,0 @@ -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json deleted file mode 100644 index 4db9e49b1d..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "@opentelemetry/plugin-notsupported-module", - "version": "1.0.0" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js deleted file mode 100644 index 3001ad4c61..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js +++ /dev/null @@ -1,25 +0,0 @@ -Object.defineProperty(exports, "__esModule", { value: true }); -const { BasePlugin } = require('../../../BasePlugin'); -const shimmer = require("shimmer"); - -class SimpleModulePlugin extends BasePlugin { - constructor() { - super(); - this.moduleName = 'notsupported-module'; - } - - patch() { - shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); - shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); - return this._moduleExports; - } - - unpatch() { - shimmer.unwrap(this._moduleExports, 'name'); - shimmer.unwrap(this._moduleExports, 'value'); - } -} -exports.SimpleModulePlugin = SimpleModulePlugin; -const plugin = new SimpleModulePlugin(); -plugin.supportedVersions = ['1.0.0']; -exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js deleted file mode 100644 index 1b22b5ce90..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js +++ /dev/null @@ -1,5 +0,0 @@ -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json deleted file mode 100644 index 59d87df350..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "@opentelemetry/plugin-simple-module", - "version": "0.0.1" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js deleted file mode 100644 index 3cfacba5fa..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js +++ /dev/null @@ -1,24 +0,0 @@ -Object.defineProperty(exports, "__esModule", { value: true }); -const { BasePlugin } = require('../../../BasePlugin'); -const shimmer = require("shimmer"); - -class SimpleModulePlugin extends BasePlugin { - constructor() { - super(); - this.moduleName = 'simple-module'; - } - - patch() { - shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); - shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); - return this._moduleExports; - } - - unpatch() { - shimmer.unwrap(this._moduleExports, 'name'); - shimmer.unwrap(this._moduleExports, 'value'); - } -} -exports.SimpleModulePlugin = SimpleModulePlugin; -const plugin = new SimpleModulePlugin(); -exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js deleted file mode 100644 index 1b22b5ce90..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js +++ /dev/null @@ -1,5 +0,0 @@ -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json deleted file mode 100644 index ca18bafa63..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "@opentelemetry/plugin-supported-module", - "version": "0.0.1" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js deleted file mode 100644 index 3a77642f79..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js +++ /dev/null @@ -1,25 +0,0 @@ -Object.defineProperty(exports, "__esModule", { value: true }); -const { BasePlugin } = require('../../../BasePlugin'); -const shimmer = require("shimmer"); - -class SimpleModulePlugin extends BasePlugin { - constructor() { - super(); - this.moduleName = 'supported-module'; - } - - patch() { - shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); - shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); - return this._moduleExports; - } - - unpatch() { - shimmer.unwrap(this._moduleExports, 'name'); - shimmer.unwrap(this._moduleExports, 'value'); - } -} -exports.SimpleModulePlugin = SimpleModulePlugin; -const plugin = new SimpleModulePlugin(); -plugin.supportedVersions = ['^0.0.1']; -exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js deleted file mode 100644 index 18c0f69a3b..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - name: () => 'already-module', - value: () => 0, -}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json deleted file mode 100644 index 7ae0ab8f09..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "already-module", - "version": "0.1.0" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js deleted file mode 100644 index 4fe98dae33..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - name: () => 'notsupported-module', - value: () => 0, -}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json deleted file mode 100644 index 9494b2866e..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "notsupported-module", - "version": "0.0.1" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js deleted file mode 100644 index 35a4110c28..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - name: () => 'random-module', - value: () => 0, -}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json deleted file mode 100644 index a5c840081b..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "random-module", - "version": "0.1.0" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js deleted file mode 100644 index 8ec2e77ffd..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - name: () => 'simple-module', - value: () => 0, -}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json deleted file mode 100644 index 2eba36a5bd..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "simple-module", - "version": "0.1.0" -} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js deleted file mode 100644 index 090d0db5fb..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - name: () => 'supported-module', - value: () => 0, -}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json deleted file mode 100644 index ffd520afda..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "supported-module", - "version": "0.0.1" -} diff --git a/packages/opentelemetry-instrumentation/test/node/utils.test.ts b/packages/opentelemetry-instrumentation/test/node/utils.test.ts deleted file mode 100644 index 8f1c4ac8c9..0000000000 --- a/packages/opentelemetry-instrumentation/test/node/utils.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as assert from 'assert'; -import * as path from 'path'; -import * as utils from '../../src/platform/node/old/utils'; - -const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); -const TEST_MODULES: Array<{ name: string; version: string | null }> = [ - { - name: 'simple-module', - version: '0.1.0', - }, - { - name: 'nonexistent-module', - version: null, - }, - { - name: 'http', - version: null, - }, -]; - -describe('Instrumentation#utils', () => { - before(() => { - utils.searchPathForTest(INSTALLED_PLUGINS_PATH); - }); - - describe('getPackageVersion', () => { - TEST_MODULES.forEach(testCase => { - it(`should return ${testCase.version} for ${testCase.name}`, () => { - assert.strictEqual( - utils.getPackageVersion(testCase.name), - testCase.version - ); - }); - }); - }); - describe('isSupportedVersion', () => { - const version = '1.0.1'; - - it('should return true when supportedVersions is not defined', () => { - assert.strictEqual(utils.isSupportedVersion('1.0.0', undefined), true); - }); - - [ - ['1.X'], - [version], - ['1.X.X', '3.X.X'], - ['^1.0.0'], - ['~1.0.0', '^0.1.0'], - ['*'], - ['>1.0.0'], - [], - ].forEach(supportedVersion => { - it(`should return true when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => { - assert.strictEqual( - utils.isSupportedVersion(version, supportedVersion), - true - ); - }); - }); - - [['0.X'], ['0.1.0'], ['0.X.X'], ['^0.1.0'], ['1.0.0'], ['<1.0.0']].forEach( - supportedVersion => { - it(`should return false when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => { - assert.strictEqual( - utils.isSupportedVersion(version, supportedVersion), - false - ); - }); - } - ); - - it("should return false when version is equal to null and supportedVersions is equal to '*'", () => { - assert.strictEqual(utils.isSupportedVersion(null as any, ['*']), false); - }); - }); -}); diff --git a/packages/opentelemetry-metrics/package.json b/packages/opentelemetry-metrics/package.json index acd48ccf05..e7c9ead7f5 100644 --- a/packages/opentelemetry-metrics/package.json +++ b/packages/opentelemetry-metrics/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/metrics", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry metrics SDK", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -40,25 +40,28 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/lodash.merge": "4.6.6", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", + "@opentelemetry/api-metrics": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/resources": "0.18.2", "lodash.merge": "^4.6.2" } } diff --git a/packages/opentelemetry-metrics/src/version.ts b/packages/opentelemetry-metrics/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-metrics/src/version.ts +++ b/packages/opentelemetry-metrics/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md index d1cc54c099..04a4b64abb 100644 --- a/packages/opentelemetry-node/README.md +++ b/packages/opentelemetry-node/README.md @@ -13,9 +13,9 @@ For manual instrumentation see the ## How auto instrumentation works This package exposes a `NodeTracerProvider`. -For loading plugins / instrumentations please use `registerInstrumentations` function from [opentelemetry-instrumentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-instrumentation) +For loading instrumentations please use `registerInstrumentations` function from [opentelemetry-instrumentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-instrumentation) -OpenTelemetry comes with a growing number of instrumentation plugins for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins)) and an API to create custom instrumentation (see [the instrumentation developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md)). +OpenTelemetry comes with a growing number of instrumentation plugins for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#instrumentations)) and an API to create custom instrumentation (see [the instrumentation developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md)). > **Please note:** This module does *not* bundle any plugins. They need to be installed separately. @@ -72,21 +72,16 @@ registerInstrumentations({ const http = require('http'); ``` -## Instrumentation / Plugin configuration - -User supplied plugin configuration is merged with the default plugin -configuration. Furthermore, custom plugins that are configured are implicitly -enabled just as default plugins are. +## Instrumentation configuration In the following example: -- the default express plugin is disabled -- the http plugin has a custom config for a `requestHook` -- the customPlugin is loaded from the user supplied path -- all default plugins are still loaded if installed. +- the express instrumentation is enabled +- the http instrumentation has a custom config for a `requestHook` ```javascript -const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); +const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); const provider = new NodeTracerProvider(); @@ -95,37 +90,18 @@ const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, instrumentations: [ - new GraphQLInstrumentation(), - // for older plugins you can just copy paste the old configuration - { - plugins: { - express: { - enabled: false, - }, - http: { - requestHook: (span, request) => { - span.setAttribute("custom request hook attribute", "request"); - }, - }, - customPlugin: { - path: "/path/to/custom/module", + new ExpressInstrumentation(), + new HttpInstrumentation({ + requestHook: (span, request) => { + span.setAttribute("custom request hook attribute", "request"); }, - }, - } + }), ], }); ``` -### Disable Plugins with Environment Variables - -Plugins can be disabled without modifying and redeploying code. -`OTEL_NO_PATCH_MODULES` accepts a -comma separated list of module names to disabled specific plugins. -The names should match what you use to `require` the module into your application. -For example, `OTEL_NO_PATCH_MODULES=pg,https` will disable the postgres plugin and the https plugin. To disable **all** plugins, set the environment variable to `*`. - ## Examples See how to automatically instrument [http](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/http) and [gRPC](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc) / [grpc-js](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc-js) using node-sdk. diff --git a/packages/opentelemetry-node/package.json b/packages/opentelemetry-node/package.json index bf49682c57..b32ece42d8 100644 --- a/packages/opentelemetry-node/package.json +++ b/packages/opentelemetry-node/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/node", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Node SDK provides automatic telemetry (tracing, metrics, etc) for Node.js applications", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -41,9 +41,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/resources": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/resources": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "@types/shimmer": "1.0.1", "@types/sinon": "9.0.11", @@ -53,16 +54,20 @@ "nyc": "15.1.0", "rimraf": "3.0.2", "shimmer": "1.2.1", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/context-async-hooks": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/tracing": "0.18.2", + "@opentelemetry/propagator-b3": "0.18.2", + "@opentelemetry/propagator-jaeger": "0.18.2", "semver": "^7.1.3" } } diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index 7afc986672..dd256689a5 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { diag } from '@opentelemetry/api'; +import { TextMapPropagator } from '@opentelemetry/api'; import { AsyncHooksContextManager, AsyncLocalStorageContextManager, } from '@opentelemetry/context-async-hooks'; +import { B3Propagator, B3InjectEncoding } from '@opentelemetry/propagator-b3'; import { BasicTracerProvider, + PROPAGATOR_FACTORY, SDKRegistrationConfig, } from '@opentelemetry/tracing'; import * as semver from 'semver'; import { NodeTracerConfig } from './config'; +import { JaegerHttpTracePropagator } from '@opentelemetry/propagator-jaeger'; /** * Register this TracerProvider for use with the OpenTelemetry API. @@ -34,14 +36,24 @@ import { NodeTracerConfig } from './config'; * @param config Configuration object for SDK registration */ export class NodeTracerProvider extends BasicTracerProvider { + protected static readonly _registeredPropagators = new Map< + string, + PROPAGATOR_FACTORY + >([ + [ + 'b3', + () => + new B3Propagator({ injectEncoding: B3InjectEncoding.SINGLE_HEADER }), + ], + [ + 'b3multi', + () => new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }), + ], + ['jaeger', () => new JaegerHttpTracePropagator()], + ]); + constructor(config: NodeTracerConfig = {}) { super(config); - if (config.plugins) { - diag.warn( - 'plugins options was removed, please use' + - ' "registerInstrumentations" to load plugins' - ); - } } register(config: SDKRegistrationConfig = {}) { @@ -55,4 +67,11 @@ export class NodeTracerProvider extends BasicTracerProvider { super.register(config); } + + protected _getPropagator(name: string): TextMapPropagator | undefined { + return ( + super._getPropagator(name) || + NodeTracerProvider._registeredPropagators.get(name)?.() + ); + } } diff --git a/packages/opentelemetry-node/src/config.ts b/packages/opentelemetry-node/src/config.ts index e5122b5ad9..caac5764c6 100644 --- a/packages/opentelemetry-node/src/config.ts +++ b/packages/opentelemetry-node/src/config.ts @@ -19,7 +19,4 @@ import { TracerConfig } from '@opentelemetry/tracing'; /** * NodeTracerConfig provides an interface for configuring a Node Tracer. */ -export interface NodeTracerConfig extends TracerConfig { - /** Plugins options deprecated */ - plugins?: unknown[]; -} +export type NodeTracerConfig = TracerConfig; diff --git a/packages/opentelemetry-node/src/version.ts b/packages/opentelemetry-node/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-node/src/version.ts +++ b/packages/opentelemetry-node/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts index 9b45531134..81fe634775 100644 --- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts +++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts @@ -20,14 +20,13 @@ import { setSpan, setSpanContext, getSpan, - diag, + propagation, } from '@opentelemetry/api'; import { AlwaysOnSampler, AlwaysOffSampler } from '@opentelemetry/core'; import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; import { Span } from '@opentelemetry/tracing'; import { Resource, TELEMETRY_SDK_RESOURCE } from '@opentelemetry/resources'; import * as assert from 'assert'; -import * as sinon from 'sinon'; import * as path from 'path'; import { ContextManager, ROOT_CONTEXT } from '@opentelemetry/api'; import { NodeTracerProvider } from '../src/NodeTracerProvider'; @@ -79,20 +78,6 @@ describe('NodeTracerProvider', () => { }); assert.ok(provider instanceof NodeTracerProvider); }); - - it('should show warning when plugins are defined', () => { - const dummyPlugin1 = {}; - const spyWarn = sinon.spy(diag, 'warn'); - - const plugins = [dummyPlugin1]; - const options = { plugins }; - provider = new NodeTracerProvider(options); - - assert.strictEqual( - spyWarn.args[0][0], - 'plugins options was removed, please use "registerInstrumentations" to load plugins' - ); - }); }); describe('.startSpan()', () => { @@ -227,4 +212,37 @@ describe('NodeTracerProvider', () => { return patchedFn(); }); }); + + describe('.register()', () => { + let originalPropagators: string | number | undefined | string[]; + beforeEach(() => { + originalPropagators = process.env.OTEL_PROPAGATORS; + }); + + afterEach(() => { + // otherwise we may assign 'undefined' (a string) + if (originalPropagators !== undefined) { + (process.env as any).OTEL_PROPAGATORS = originalPropagators; + } else { + delete (process.env as any).OTEL_PROPAGATORS; + } + }); + + it('should allow propagators as per the specification', () => { + (process.env as any).OTEL_PROPAGATORS = 'b3,b3multi,jaeger'; + + const provider = new NodeTracerProvider(); + provider.register(); + + assert.deepStrictEqual(propagation.fields(), [ + 'b3', + 'x-b3-traceid', + 'x-b3-spanid', + 'x-b3-flags', + 'x-b3-sampled', + 'x-b3-parentspanid', + 'uber-trace-id', + ]); + }); + }); }); diff --git a/packages/opentelemetry-node/tsconfig.json b/packages/opentelemetry-node/tsconfig.json index 38e281f4ba..353ee432d2 100644 --- a/packages/opentelemetry-node/tsconfig.json +++ b/packages/opentelemetry-node/tsconfig.json @@ -15,6 +15,12 @@ { "path": "../opentelemetry-core" }, + { + "path": "../opentelemetry-propagator-b3" + }, + { + "path": "../opentelemetry-propagator-jaeger" + }, { "path": "../opentelemetry-resources" }, diff --git a/packages/opentelemetry-plugin-grpc-js/.eslintignore b/packages/opentelemetry-plugin-grpc-js/.eslintignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/opentelemetry-plugin-grpc-js/.eslintrc.js b/packages/opentelemetry-plugin-grpc-js/.eslintrc.js deleted file mode 100644 index f726f3becb..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - "env": { - "mocha": true, - "node": true - }, - ...require('../../eslint.config.js') -} diff --git a/packages/opentelemetry-plugin-grpc-js/.npmignore b/packages/opentelemetry-plugin-grpc-js/.npmignore deleted file mode 100644 index 9505ba9450..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/coverage -/doc -/test diff --git a/packages/opentelemetry-plugin-grpc-js/LICENSE b/packages/opentelemetry-plugin-grpc-js/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/packages/opentelemetry-plugin-grpc-js/README.md b/packages/opentelemetry-plugin-grpc-js/README.md deleted file mode 100644 index 553b1dc187..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# OpenTelemetry @grpc/grpc-js Instrumentation for Node.js - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![devDependencies][devDependencies-image]][devDependencies-url] -[![Apache License][license-image]][license-image] - -This module provides automatic instrumentation for [`@grpc/grpc-js`](https://grpc.io/blog/grpc-js-1.0/). Currently, version [`1.x`](https://www.npmjs.com/package/@grpc/grpc-js?activeTab=versions) of `@grpc/grpc-js` is supported. - -For automatic instrumentation see the -[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node) package. - -## Installation - -```sh -npm install --save @opentelemetry/plugin-grpc-js -``` - -## Usage - -OpenTelemetry gRPC Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems when working with [gRPC](https://www.npmjs.com/package/@grpc/grpc-js). - -To load a specific plugin (**gRPC** in this case), specify it in the Node Tracer's configuration. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider({ - plugins: { - '@grpc/grpc-js': { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-grpc-js', - // gRPC-js plugin options - } - } -}); -``` - -To load all of the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider(); -``` - - - -### gRPC-js Plugin Options - -gRPC-js plugin accepts the following configuration: - -| Options | Type | Description | -| ------- | ---- | ----------- | -| [`ignoreGrpcMethods`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-grpc-js/src/types.ts#L24) | `IgnoreMatcher[]` | gRPC plugin will not trace any methods that match anything in this list. You may pass a string (case-insensitive match), a `RegExp` object, or a filter function. | - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-grpc-js -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-grpc-js -[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-grpc-js&type=dev -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-grpc-js&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-grpc-js -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-grpc-js.svg diff --git a/packages/opentelemetry-plugin-grpc-js/package.json b/packages/opentelemetry-plugin-grpc-js/package.json deleted file mode 100644 index b82121ac09..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/package.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "@opentelemetry/plugin-grpc-js", - "version": "0.18.0", - "description": "OpenTelemetry @grpc/grpc-js automatic instrumentation package.", - "main": "build/src/index.js", - "types": "build/src/index.d.ts", - "repository": "open-telemetry/opentelemetry-js", - "scripts": { - "compile": "tsc --build", - "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", - "test:deubg": "ts-mocha --inspect-brk -p tsconfig.json test/**/*.test.ts", - "tdd": "npm run test -- --watch-extensions ts --watch", - "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" - }, - "keywords": [ - "opentelemetry", - "grpc", - "grpc-js", - "nodejs", - "tracing", - "profiling", - "plugin" - ], - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - }, - "files": [ - "build/src/**/*.js", - "build/src/**/*.js.map", - "build/src/**/*.d.ts", - "doc", - "LICENSE", - "README.md" - ], - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "@grpc/grpc-js": "1.2.12", - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/grpc-utils": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@types/mocha": "8.2.2", - "@types/node": "14.14.35", - "@types/semver": "7.3.4", - "@types/shimmer": "1.0.1", - "@types/sinon": "9.0.11", - "codecov": "3.8.1", - "gts": "3.1.0", - "mocha": "7.2.0", - "nyc": "15.1.0", - "rimraf": "3.0.2", - "semver": "7.3.5", - "sinon": "9.2.4", - "ts-mocha": "8.0.0", - "ts-node": "9.1.1", - "typescript": "4.2.3" - }, - "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "shimmer": "1.2.1" - } -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/loadPackageDefinition.ts b/packages/opentelemetry-plugin-grpc-js/src/client/loadPackageDefinition.ts deleted file mode 100644 index f300d5d9d3..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/client/loadPackageDefinition.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { diag } from '@opentelemetry/api'; -import { GrpcJsPlugin } from '../grpcJs'; -import type * as grpcJs from '@grpc/grpc-js'; -import type { PackageDefinition } from '@grpc/grpc-js/build/src/make-client'; -import * as shimmer from 'shimmer'; -import { getMethodsToWrap, getPatchedClientMethods } from './utils'; - -/** - * Entry point for client patching for grpc.loadPackageDefinition(...) - * @param this - GrpcJsPlugin - */ -export function patchLoadPackageDefinition(this: GrpcJsPlugin) { - return (original: typeof grpcJs.loadPackageDefinition) => { - const plugin = this; - - diag.debug('patching loadPackageDefinition'); - - return function patchedLoadPackageDefinition( - this: null, - packageDef: PackageDefinition - ) { - const result: grpcJs.GrpcObject = original.call( - this, - packageDef - ) as grpcJs.GrpcObject; - _patchLoadedPackage.call(plugin, result); - return result; - } as typeof grpcJs.loadPackageDefinition; - }; -} - -/** - * Utility function to patch *all* functions loaded through a proto file. - * Recursively searches for Client classes and patches all methods, reversing the - * parsing done by grpc.loadPackageDefinition - * https://github.com/grpc/grpc-node/blob/1d14203c382509c3f36132bd0244c99792cb6601/packages/grpc-js/src/make-client.ts#L200-L217 - */ -function _patchLoadedPackage( - this: GrpcJsPlugin, - result: grpcJs.GrpcObject -): void { - Object.values(result).forEach(service => { - if (typeof service === 'function') { - shimmer.massWrap( - service.prototype, - getMethodsToWrap.call(this, service, service.service), - getPatchedClientMethods.call(this) - ); - } else if (typeof service.format !== 'string') { - // GrpcObject - _patchLoadedPackage.call(this, service as grpcJs.GrpcObject); - } - }); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/patchClient.ts b/packages/opentelemetry-plugin-grpc-js/src/client/patchClient.ts deleted file mode 100644 index 795c51d770..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/client/patchClient.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { diag } from '@opentelemetry/api'; -import { GrpcJsPlugin } from '../grpcJs'; -import type * as grpcJs from '@grpc/grpc-js'; -import * as shimmer from 'shimmer'; -import { getMethodsToWrap, getPatchedClientMethods } from './utils'; - -type MakeClientConstructorFunction = typeof grpcJs.makeGenericClientConstructor; - -/** - * Entry point for applying client patches to `grpc.makeClientConstructor(...)` equivalents - * @param this GrpcJsPlugin - */ -export function patchClient( - this: GrpcJsPlugin -): (original: MakeClientConstructorFunction) => MakeClientConstructorFunction { - const plugin = this; - return (original: MakeClientConstructorFunction) => { - diag.debug('patching client'); - return function makeClientConstructor( - this: typeof grpcJs.Client, - methods: grpcJs.ServiceDefinition, - serviceName: string, - options?: object - ) { - const client = original.call(this, methods, serviceName, options); - shimmer.massWrap( - client.prototype, - getMethodsToWrap.call(plugin, client, methods), - getPatchedClientMethods.call(plugin) - ); - return client; - }; - }; -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts deleted file mode 100644 index dc696b88fb..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GrpcJsPlugin } from '../grpcJs'; -import type { GrpcClientFunc, SendUnaryDataCallback } from '../types'; -import { - SpanKind, - Span, - SpanStatusCode, - SpanStatus, - propagation, - context, - setSpan, - diag, -} from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; -import { - grpcStatusCodeToSpanStatus, - grpcStatusCodeToOpenTelemetryStatusCode, - CALL_SPAN_ENDED, - methodIsIgnored, -} from '../utils'; -import { EventEmitter } from 'events'; - -/** - * Parse a package method list and return a list of methods to patch - * with both possible casings e.g. "TestMethod" & "testMethod" - */ -export function getMethodsToWrap( - this: GrpcJsPlugin, - client: typeof grpcJs.Client, - methods: { [key: string]: { originalName?: string } } -): string[] { - const methodList: string[] = []; - - // For a method defined in .proto as "UnaryMethod" - Object.entries(methods).forEach(([name, { originalName }]) => { - if (!methodIsIgnored(name, this._config.ignoreGrpcMethods)) { - methodList.push(name); // adds camel case method name: "unaryMethod" - if ( - originalName && - // eslint-disable-next-line no-prototype-builtins - client.prototype.hasOwnProperty(originalName) && - name !== originalName // do not add duplicates - ) { - // adds original method name: "UnaryMethod", - methodList.push(originalName); - } - } - }); - - return methodList; -} - -/** - * Parse initial client call properties and start a span to trace its execution - */ -export function getPatchedClientMethods( - this: GrpcJsPlugin -): (original: GrpcClientFunc) => () => EventEmitter { - const plugin = this; - return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); - return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace('/', '')}`; - const args = [...arguments]; - const metadata = getMetadata.call(plugin, original, args); - const span = plugin.tracer.startSpan(name, { - kind: SpanKind.CLIENT, - }); - return context.with(setSpan(context.active(), span), () => - makeGrpcClientRemoteCall(original, args, metadata, this)(span) - ); - }; - }; -} - -/** - * Execute grpc client call. Apply completitionspan properties and end the - * span on callback or receiving an emitted event. - */ -export function makeGrpcClientRemoteCall( - original: GrpcClientFunc, - args: unknown[], - metadata: grpcJs.Metadata, - self: grpcJs.Client -): (span: Span) => EventEmitter { - /** - * Patches a callback so that the current span for this trace is also ended - * when the callback is invoked. - */ - function patchedCallback( - span: Span, - callback: SendUnaryDataCallback - ) { - const wrappedFn: SendUnaryDataCallback = ( - err: grpcJs.ServiceError | null, - res - ) => { - if (err) { - if (err.code) { - span.setStatus(grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); - } - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - } - - span.end(); - callback(err, res); - }; - return context.bind(wrappedFn); - } - - return (span: Span) => { - // if unary or clientStream - if (!original.responseStream) { - const callbackFuncIndex = args.findIndex(arg => { - return typeof arg === 'function'; - }); - if (callbackFuncIndex !== -1) { - args[callbackFuncIndex] = patchedCallback( - span, - args[callbackFuncIndex] as SendUnaryDataCallback - ); - } - } - - span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, - }); - - setSpanContext(metadata); - const call = original.apply(self, args); - - // if server stream or bidi - if (original.responseStream) { - // Both error and status events can be emitted - // the first one emitted set spanEnded to true - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - span.end(); - spanEnded = true; - } - }; - context.bind(call); - call.on('error', (err: grpcJs.ServiceError) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - - endSpan(); - }); - - call.on('status', (status: SpanStatus) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - call[CALL_SPAN_ENDED] = true; - - span.setStatus(grpcStatusCodeToSpanStatus(status.code)); - - endSpan(); - }); - } - return call; - }; -} - -/** - * Returns the metadata argument from user provided arguments (`args`) - */ -function getMetadata( - this: GrpcJsPlugin, - original: GrpcClientFunc, - args: Array -): grpcJs.Metadata { - let metadata: grpcJs.Metadata; - - // This finds an instance of Metadata among the arguments. - // A possible issue that could occur is if the 'options' parameter from - // the user contains an '_internal_repr' as well as a 'getMap' function, - // but this is an extremely rare case. - let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { - return ( - arg && - typeof arg === 'object' && - (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === 'function' - ); - }); - if (metadataIndex === -1) { - metadata = new this._moduleExports.Metadata(); - if (!original.requestStream) { - // unary or server stream - metadataIndex = 1; - } else { - // client stream or bidi - metadataIndex = 0; - } - args.splice(metadataIndex, 0, metadata); - } else { - metadata = args[metadataIndex] as grpcJs.Metadata; - } - return metadata; -} - -/** - * Inject opentelemetry trace context into `metadata` for use by another - * grpc receiver - * @param metadata - */ -export function setSpanContext(metadata: grpcJs.Metadata): void { - propagation.inject(context.active(), metadata, { - set: (metadata, k, v) => metadata.set(k, v as grpcJs.MetadataValue), - }); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/grpcJs.ts b/packages/opentelemetry-plugin-grpc-js/src/grpcJs.ts deleted file mode 100644 index ed647040d6..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/grpcJs.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type * as grpcJs from '@grpc/grpc-js'; -import { BasePlugin } from '@opentelemetry/core'; -import * as shimmer from 'shimmer'; -import { patchClient, patchLoadPackageDefinition } from './client'; -import { patchServer } from './server'; -import { VERSION } from './version'; -import { diag, Tracer } from '@opentelemetry/api'; -import { GrpcPluginOptions } from './types'; - -/** - * @grpc/grpc-js gRPC instrumentation plugin for Opentelemetry - * https://www.npmjs.com/package/@grpc/grpc-js - */ -export class GrpcJsPlugin extends BasePlugin { - static readonly component = '@grpc/grpc-js'; - - readonly supportedVersions = ['1.*']; - - protected _config!: GrpcPluginOptions; - - constructor(readonly moduleName: string) { - super('@opentelemetry/plugin-grpc-js', VERSION); - } - - /** - * @internal - * Public reference to the protected BasePlugin `_tracer` instance to be used by this - * plugin's external helper functions - */ - get tracer(): Tracer { - return this._tracer; - } - - protected patch(): typeof grpcJs { - // Patch Server methods - shimmer.wrap( - this._moduleExports.Server.prototype, - 'register', - patchServer.call(this) - ); - - // Patch Client methods - shimmer.wrap( - this._moduleExports, - 'makeClientConstructor', - patchClient.call(this) - ); - shimmer.wrap( - this._moduleExports, - 'makeGenericClientConstructor', - patchClient.call(this) - ); - shimmer.wrap( - this._moduleExports, - 'loadPackageDefinition', - patchLoadPackageDefinition.call(this) - ); - - return this._moduleExports; - } - - protected unpatch(): void { - diag.debug('removing patch to %s@%s', this.moduleName, this.version); - - // Unpatch server - shimmer.unwrap(this._moduleExports.Server.prototype, 'register'); - - // Unpatch client - shimmer.unwrap(this._moduleExports, 'makeClientConstructor'); - shimmer.unwrap(this._moduleExports, 'makeGenericClientConstructor'); - shimmer.unwrap(this._moduleExports, 'loadPackageDefinition'); - } -} - -export const plugin = new GrpcJsPlugin(GrpcJsPlugin.component); diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts deleted file mode 100644 index 431f659966..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import type { ServerCallWithMeta, SendUnaryDataCallback } from '../types'; -import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; -import type { GrpcJsPlugin } from '../grpcJs'; -import type * as grpcJs from '@grpc/grpc-js'; - -/** - * Handle patching for clientStream and unary type server handlers - */ -export function clientStreamAndUnaryHandler( - plugin: GrpcJsPlugin, - span: Span, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback, - original: - | grpcJs.handleUnaryCall - | grpcJs.ClientReadableStream -): void { - const patchedCallback: SendUnaryDataCallback = ( - err: grpcJs.ServiceError | null, - value?: ResponseType - ) => { - if (err) { - if (err.code) { - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); - } - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - } - - span.end(); - return callback(err, value); - }; - - context.bind(call); - return (original as Function).call({}, call, patchedCallback); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts deleted file mode 100644 index 06ff464dfe..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type * as grpcJs from '@grpc/grpc-js'; -import type { HandleCall } from '@grpc/grpc-js/build/src/server-call'; -import { GrpcJsPlugin } from '../grpcJs'; -import * as shimmer from 'shimmer'; -import { - ServerCallWithMeta, - SendUnaryDataCallback, - IgnoreMatcher, -} from '../types'; -import { - context, - SpanOptions, - SpanKind, - propagation, - Span, - ROOT_CONTEXT, - setSpan, - diag, -} from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; -import { clientStreamAndUnaryHandler } from './clientStreamAndUnary'; -import { serverStreamAndBidiHandler } from './serverStreamAndBidi'; -import { methodIsIgnored } from '../utils'; - -type ServerRegisterFunction = typeof grpcJs.Server.prototype.register; - -/** - * Patch for grpc.Server.prototype.register(...) function. Provides auto-instrumentation for - * client_stream, server_stream, bidi, unary server handler calls. - */ -export function patchServer( - this: GrpcJsPlugin -): (originalRegister: ServerRegisterFunction) => ServerRegisterFunction { - return (originalRegister: ServerRegisterFunction) => { - const plugin = this; - const config = this._config; - - diag.debug('patched gRPC server'); - return function register( - this: grpcJs.Server, - name: string, - handler: HandleCall, - serialize: grpcJs.serialize, - deserialize: grpcJs.deserialize, - type: string - ): boolean { - const originalRegisterResult = originalRegister.call( - this, - name, - handler, - serialize, - deserialize, - type - ); - const handlerSet = this['handlers'].get(name); - - shimmer.wrap( - handlerSet, - 'func', - (originalFunc: HandleCall) => { - return function func( - this: typeof handlerSet, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback - ) { - const self = this; - - if ( - shouldNotTraceServerCall( - call.metadata, - name, - config.ignoreGrpcMethods - ) - ) { - return handleUntracedServerFunction( - type, - originalFunc, - call, - callback - ); - } - - const spanName = `grpc.${name.replace('/', '')}`; - const spanOptions: SpanOptions = { - kind: SpanKind.SERVER, - }; - - diag.debug('patch func: %s', JSON.stringify(spanOptions)); - - context.with( - propagation.extract(ROOT_CONTEXT, call.metadata, { - get: (carrier, key) => carrier.get(key).map(String), - keys: carrier => Object.keys(carrier.getMap()), - }), - () => { - const span = plugin.tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, - }); - - context.with(setSpan(context.active(), span), () => { - handleServerFunction.call( - self, - plugin, - span, - type, - originalFunc, - call, - callback - ); - }); - } - ); - }; - } - ); - return originalRegisterResult; - } as typeof grpcJs.Server.prototype.register; - }; -} - -/** - * Returns true if the server call should not be traced. - */ -function shouldNotTraceServerCall( - metadata: grpcJs.Metadata, - methodName: string, - ignoreGrpcMethods?: IgnoreMatcher[] -): boolean { - const parsedName = methodName.split('/'); - return methodIsIgnored( - parsedName[parsedName.length - 1] || methodName, - ignoreGrpcMethods - ); -} - -/** - * Patch callback or EventEmitter provided by `originalFunc` and set appropriate `span` - * properties based on its result. - */ -function handleServerFunction( - this: unknown, - plugin: GrpcJsPlugin, - span: Span, - type: string, - originalFunc: HandleCall, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback -): void { - switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': - return clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc as - | grpcJs.handleUnaryCall - | grpcJs.ClientReadableStream - ); - case 'serverStream': - case 'server_stream': - case 'bidi': - return serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc as - | grpcJs.handleBidiStreamingCall - | grpcJs.handleServerStreamingCall - ); - default: - break; - } -} - -/** - * Does not patch any callbacks or EventEmitters to omit tracing on requests - * that should not be traced. - */ -function handleUntracedServerFunction( - type: string, - originalFunc: HandleCall, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback -): void { - switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': - return (originalFunc as Function).call({}, call, callback); - case 'serverStream': - case 'server_stream': - case 'bidi': - return (originalFunc as Function).call({}, call); - default: - break; - } -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts deleted file mode 100644 index 18341ff0ee..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; -import type { GrpcJsPlugin } from '../grpcJs'; -import { GrpcEmitter } from '../types'; -import { - CALL_SPAN_ENDED, - grpcStatusCodeToOpenTelemetryStatusCode, -} from '../utils'; - -/** - * Handle patching for serverStream and Bidi type server handlers - */ -export function serverStreamAndBidiHandler( - plugin: GrpcJsPlugin, - span: Span, - call: GrpcEmitter, - original: - | grpcJs.handleBidiStreamingCall - | grpcJs.handleServerStreamingCall -): void { - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - spanEnded = true; - span.end(); - } - }; - - context.bind(call); - call.on('finish', () => { - // @grpc/js does not expose a way to check if this call also emitted an error, - // e.g. call.status.code !== 0 - if (call[CALL_SPAN_ENDED]) { - return; - } - - // Set the "grpc call had an error" flag - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: SpanStatusCode.OK, - }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - - endSpan(); - }); - - call.on('error', (err: grpcJs.ServiceError) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - - // Set the "grpc call had an error" flag - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - }); - - // Types of parameters 'call' and 'call' are incompatible. - return (original as Function).call({}, call); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/types.ts b/packages/opentelemetry-plugin-grpc-js/src/types.ts deleted file mode 100644 index c9104eb08d..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/types.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type * as grpcJs from '@grpc/grpc-js'; -import type { EventEmitter } from 'events'; -import type { CALL_SPAN_ENDED } from './utils'; -import { PluginConfig } from '@opentelemetry/core'; - -export type IgnoreMatcher = string | RegExp | ((str: string) => boolean); - -export interface GrpcPluginOptions extends PluginConfig { - /* Omits tracing on any gRPC methods that match any of - * the IgnoreMatchers in the ignoreGrpcMethods list - */ - ignoreGrpcMethods?: IgnoreMatcher[]; -} - -/** - * Server Unary callback type - */ -export type SendUnaryDataCallback = grpcJs.requestCallback; - -/** - * Intersection type of all grpc server call types - */ -export type ServerCall = - | grpcJs.ServerUnaryCall - | grpcJs.ServerReadableStream - | grpcJs.ServerWritableStream - | grpcJs.ServerDuplexStream; - -/** - * {@link ServerCall} ServerCall extended with misc. missing utility types - */ -export type ServerCallWithMeta = ServerCall & { - metadata: grpcJs.Metadata; -}; - -/** - * EventEmitter with span ended symbol indicator - */ -export type GrpcEmitter = EventEmitter & { [CALL_SPAN_ENDED]?: boolean }; - -/** - * Grpc client callback function extended with missing utility types - */ -export type GrpcClientFunc = ((...args: unknown[]) => GrpcEmitter) & { - path: string; - requestStream: boolean; - responseStream: boolean; -}; diff --git a/packages/opentelemetry-plugin-grpc-js/src/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/utils.ts deleted file mode 100644 index cb48d9f710..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/utils.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanStatusCode, SpanStatus } from '@opentelemetry/api'; -import type * as grpcTypes from '@grpc/grpc-js'; // For types only -import { IgnoreMatcher } from './types'; - -/** - * Symbol to include on grpc call if it has already emitted an error event. - * grpc events that emit 'error' will also emit 'finish' and so only the - * error event should be processed. - */ -export const CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); - -/** - * Convert a grpc status code to an opentelemetry SpanStatus code. - * @param status - */ -export const grpcStatusCodeToOpenTelemetryStatusCode = ( - status?: grpcTypes.status -): SpanStatusCode => { - if (status !== undefined && status === 0) { - return SpanStatusCode.OK; - } - return SpanStatusCode.ERROR; -}; - -/** - * Convert grpc status code to an opentelemetry SpanStatus object. - * @param status - */ -export const grpcStatusCodeToSpanStatus = (status: number): SpanStatus => { - return { code: grpcStatusCodeToOpenTelemetryStatusCode(status) }; -}; - -/** - * Returns true if methodName matches pattern - * @param methodName the name of the method - * @param pattern Match pattern - */ -const satisfiesPattern = ( - methodName: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern.toLowerCase() === methodName.toLowerCase(); - } else if (pattern instanceof RegExp) { - return pattern.test(methodName); - } else if (typeof pattern === 'function') { - return pattern(methodName); - } else { - return false; - } -}; - -/** - * Returns true if the current plugin configuration - * ignores the given method. - * @param methodName the name of the method - * @param ignoredMethods a list of matching patterns - * @param onException an error handler for matching exceptions - */ -export const methodIsIgnored = ( - methodName: string, - ignoredMethods?: IgnoreMatcher[] -): boolean => { - if (!ignoredMethods) { - // No ignored gRPC methods - return false; - } - - for (const pattern of ignoredMethods) { - if (satisfiesPattern(methodName, pattern)) { - return true; - } - } - - return false; -}; diff --git a/packages/opentelemetry-plugin-grpc-js/tsconfig.json b/packages/opentelemetry-plugin-grpc-js/tsconfig.json deleted file mode 100644 index fed694f718..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "build" - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "references": [ - { - "path": "../opentelemetry-context-async-hooks" - }, - { - "path": "../opentelemetry-core" - }, - { - "path": "../opentelemetry-grpc-utils" - }, - { - "path": "../opentelemetry-node" - }, - { - "path": "../opentelemetry-semantic-conventions" - }, - { - "path": "../opentelemetry-tracing" - } - ] -} diff --git a/packages/opentelemetry-plugin-grpc/.eslintignore b/packages/opentelemetry-plugin-grpc/.eslintignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/packages/opentelemetry-plugin-grpc/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/opentelemetry-plugin-grpc/.eslintrc.js b/packages/opentelemetry-plugin-grpc/.eslintrc.js deleted file mode 100644 index f726f3becb..0000000000 --- a/packages/opentelemetry-plugin-grpc/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - "env": { - "mocha": true, - "node": true - }, - ...require('../../eslint.config.js') -} diff --git a/packages/opentelemetry-plugin-grpc/.npmignore b/packages/opentelemetry-plugin-grpc/.npmignore deleted file mode 100644 index 9505ba9450..0000000000 --- a/packages/opentelemetry-plugin-grpc/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/coverage -/doc -/test diff --git a/packages/opentelemetry-plugin-grpc/LICENSE b/packages/opentelemetry-plugin-grpc/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/packages/opentelemetry-plugin-grpc/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/packages/opentelemetry-plugin-grpc/README.md b/packages/opentelemetry-plugin-grpc/README.md deleted file mode 100644 index eeed5a21d3..0000000000 --- a/packages/opentelemetry-plugin-grpc/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# OpenTelemetry gRPC Instrumentation for Node.js - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![devDependencies][devDependencies-image]][devDependencies-url] -[![Apache License][license-image]][license-image] - -This module provides automatic instrumentation for [`grpc`](https://grpc.github.io/grpc/node/). Currently, version [`1.x`](https://www.npmjs.com/package/grpc?activeTab=versions) of the Node.js gRPC library is supported. - -For automatic instrumentation see the -[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node) package. - -## Installation - -```sh -npm install --save @opentelemetry/plugin-grpc -``` - -## Usage - -OpenTelemetry gRPC Instrumentation allows the user to automatically collect trace data and export them to the backend of choice, to give observability to distributed systems when working with [gRPC](https://www.npmjs.com/package/grpc). - -To load a specific plugin (**gRPC** in this case), specify it in the Node Tracer's configuration. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider({ - plugins: { - grpc: { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-grpc', - // gRPC plugin options - } - } -}); -``` - -To load all of the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. - -```javascript -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider(); -``` - -See [examples/grpc](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/grpc) for a short example. - -### gRPC Plugin Options - -gRPC plugin accepts the following configuration: - -| Options | Type | Description | -| ------- | ---- | ----------- | -| [`ignoreGrpcMethods`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-grpc/src/types.ts#L32) | `IgnoreMatcher[]` | gRPC plugin will not trace any methods that match anything in this list. You may pass a string (case-insensitive match), a `RegExp` object, or a filter function. | - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-grpc -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-grpc -[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-grpc&type=dev -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-grpc&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-grpc -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-grpc.svg diff --git a/packages/opentelemetry-plugin-grpc/package.json b/packages/opentelemetry-plugin-grpc/package.json deleted file mode 100644 index ebbcd22e72..0000000000 --- a/packages/opentelemetry-plugin-grpc/package.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "@opentelemetry/plugin-grpc", - "version": "0.18.0", - "description": "OpenTelemetry grpc automatic instrumentation package.", - "main": "build/src/index.js", - "types": "build/src/index.d.ts", - "repository": "open-telemetry/opentelemetry-js", - "scripts": { - "compile": "tsc --build", - "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", - "tdd": "npm run test -- --watch-extensions ts --watch", - "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" - }, - "keywords": [ - "opentelemetry", - "grpc", - "nodejs", - "tracing", - "profiling", - "plugin" - ], - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - }, - "files": [ - "build/src/**/*.js", - "build/src/**/*.js.map", - "build/src/**/*.d.ts", - "doc", - "LICENSE", - "README.md" - ], - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/grpc-utils": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@types/mocha": "8.2.2", - "@types/node": "14.14.35", - "@types/semver": "7.3.4", - "@types/shimmer": "1.0.1", - "@types/sinon": "9.0.11", - "codecov": "3.8.1", - "grpc": "1.24.6", - "gts": "3.1.0", - "mocha": "7.2.0", - "node-pre-gyp": "0.17.0", - "nyc": "15.1.0", - "rimraf": "3.0.2", - "semver": "7.3.5", - "sinon": "9.2.4", - "ts-mocha": "8.0.0", - "ts-node": "9.1.1", - "typescript": "4.2.3" - }, - "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "shimmer": "^1.2.1" - } -} diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts deleted file mode 100644 index 9836d8bd22..0000000000 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - SpanStatusCode, - context, - propagation, - Span, - SpanKind, - SpanOptions, - SpanStatus, - ROOT_CONTEXT, - setSpan, - diag, -} from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; -import { BasePlugin } from '@opentelemetry/core'; -import * as events from 'events'; -import * as grpcTypes from 'grpc'; -import * as path from 'path'; -import * as shimmer from 'shimmer'; -import { - grpc, - GrpcClientFunc, - GrpcInternalClientTypes, - GrpcPluginOptions, - ModuleExportsMapping, - SendUnaryDataCallback, - ServerCallWithMeta, -} from './types'; -import { - findIndex, - _grpcStatusCodeToOpenTelemetryStatusCode, - _grpcStatusCodeToSpanStatus, - _methodIsIgnored, -} from './utils'; -import { VERSION } from './version'; - -/** The metadata key under which span context is stored as a binary value. */ -export const GRPC_TRACE_KEY = 'grpc-trace-bin'; - -let grpcClientModule: GrpcInternalClientTypes; - -export class GrpcPlugin extends BasePlugin { - static readonly component = 'grpc'; - readonly supportedVersions = ['1.*']; - - protected _config!: GrpcPluginOptions; - - constructor(readonly moduleName: string, readonly version: string) { - super('@opentelemetry/plugin-grpc', VERSION); - this._config = {}; - } - - protected readonly _internalFilesList: ModuleExportsMapping = { - '0.13 - 1.6': { client: 'src/node/src/client.js' }, - '^1.7': { client: 'src/client.js' }, - }; - protected readonly _basedir = basedir; - - protected patch(): typeof grpcTypes { - diag.debug('applying patch to %s@%s', this.moduleName, this.version); - - if (this._moduleExports.Server) { - shimmer.wrap( - this._moduleExports.Server.prototype, - 'register', - this._patchServer() as any - ); - } - - // Wrap the externally exported client constructor - shimmer.wrap( - this._moduleExports, - 'makeGenericClientConstructor', - this._patchClient() - ); - - if (this._internalFilesExports['client']) { - grpcClientModule = this._internalFilesExports[ - 'client' - ] as GrpcInternalClientTypes; - - // Wrap the internally used client constructor - shimmer.wrap( - grpcClientModule, - 'makeClientConstructor', - this._patchClient() - ); - } - - return this._moduleExports; - } - protected unpatch(): void { - diag.debug('removing patch to %s@%s', this.moduleName, this.version); - - if (this._moduleExports.Server) { - shimmer.unwrap(this._moduleExports.Server.prototype, 'register'); - } - - shimmer.unwrap(this._moduleExports, 'makeGenericClientConstructor'); - - if (grpcClientModule) { - shimmer.unwrap(grpcClientModule, 'makeClientConstructor'); - } - } - - private _setSpanContext(metadata: grpcTypes.Metadata): void { - propagation.inject(context.active(), metadata, { - set: (metadata, k, v) => metadata.set(k, v as grpcTypes.MetadataValue), - }); - } - - private _patchServer() { - return (originalRegister: typeof grpcTypes.Server.prototype.register) => { - const plugin = this; - diag.debug('patched gRPC server'); - - return function register( - this: grpcTypes.Server & { handlers: any }, - name: string, - handler: grpcTypes.handleCall, - serialize: grpcTypes.serialize, - deserialize: grpcTypes.deserialize, - type: string - ) { - const originalResult = originalRegister.apply(this, arguments as any); - const handlerSet = this.handlers[name]; - - shimmer.wrap( - handlerSet, - 'func', - (originalFunc: grpcTypes.handleCall) => { - return function func( - this: typeof handlerSet, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback - ) { - const self = this; - if (plugin._shouldNotTraceServerCall(call, name)) { - switch (type) { - case 'unary': - case 'client_stream': - return (originalFunc as Function).call( - self, - call, - callback - ); - case 'server_stream': - case 'bidi': - return (originalFunc as Function).call(self, call); - default: - return originalResult; - } - } - const spanName = `grpc.${name.replace('/', '')}`; - const spanOptions: SpanOptions = { - kind: SpanKind.SERVER, - }; - - diag.debug('patch func: %s', JSON.stringify(spanOptions)); - - context.with( - propagation.extract(ROOT_CONTEXT, call.metadata, { - get: (metadata, key) => metadata.get(key).map(String), - keys: metadata => Object.keys(metadata.getMap()), - }), - () => { - const span = plugin._tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, - }); - - context.with(setSpan(context.active(), span), () => { - switch (type) { - case 'unary': - case 'client_stream': - return plugin._clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc, - self - ); - case 'server_stream': - case 'bidi': - return plugin._serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc, - self - ); - default: - break; - } - }); - } - ); - }; - } - ); - - return originalResult; - }; - }; - } - - /** - * Returns true if the server call should not be traced. - */ - private _shouldNotTraceServerCall( - call: ServerCallWithMeta, - name: string - ): boolean { - const parsedName = name.split('/'); - return _methodIsIgnored( - parsedName[parsedName.length - 1] || name, - this._config.ignoreGrpcMethods - ); - } - - private _clientStreamAndUnaryHandler( - plugin: GrpcPlugin, - span: Span, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback, - original: - | grpcTypes.handleCall - | grpcTypes.ClientReadableStream, - self: {} - ) { - function patchedCallback( - err: grpcTypes.ServiceError, - value: any, - trailer: grpcTypes.Metadata, - flags: grpcTypes.writeFlags - ) { - if (err) { - if (err.code) { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); - } - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - plugin._moduleExports.status.OK.toString() - ); - } - span.addEvent('received'); - - // end the span - span.end(); - return callback(err, value, trailer, flags); - } - - context.bind(call); - return (original as Function).call(self, call, patchedCallback); - } - - private _serverStreamAndBidiHandler( - plugin: GrpcPlugin, - span: Span, - call: ServerCallWithMeta, - original: grpcTypes.handleCall, - self: {} - ) { - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - spanEnded = true; - span.end(); - } - }; - - context.bind(call); - call.on('finish', () => { - span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - call.status.code.toString() - ); - - // if there is an error, span will be ended on error event, otherwise end it here - if (call.status.code === 0) { - span.addEvent('finished'); - endSpan(); - } - }); - - call.on('error', (err: grpcTypes.ServiceError) => { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.addEvent('finished with error'); - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - }); - - return (original as any).call(self, call); - } - - private _patchClient() { - const plugin = this; - return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug('patching client'); - return function makeClientConstructor( - this: typeof grpcTypes.Client, - methods: { [key: string]: { originalName?: string } }, - _serviceName: string, - _options: grpcTypes.GenericClientOptions - ) { - const client = original.apply(this, arguments as any); - shimmer.massWrap( - client.prototype as never, - plugin._getMethodsToWrap(client, methods) as never[], - plugin._getPatchedClientMethods() as any - ); - return client; - } as never; - }; - } - - private _getMethodsToWrap( - client: typeof grpcTypes.Client, - methods: { [key: string]: { originalName?: string } } - ): string[] { - const methodList: string[] = []; - - // For a method defined in .proto as "UnaryMethod" - Object.entries(methods).forEach(([name, { originalName }]) => { - if (!_methodIsIgnored(name, this._config.ignoreGrpcMethods)) { - methodList.push(name); // adds camel case method name: "unaryMethod" - if ( - originalName && - // eslint-disable-next-line no-prototype-builtins - client.prototype.hasOwnProperty(originalName) && - name !== originalName // do not add duplicates - ) { - // adds original method name: "UnaryMethod", - methodList.push(originalName); - } - } - }); - return methodList; - } - - private _getPatchedClientMethods() { - const plugin = this; - return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); - return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace('/', '')}`; - const args = Array.prototype.slice.call(arguments); - const metadata = plugin._getMetadata(original, args); - const span = plugin._tracer.startSpan(name, { - kind: SpanKind.CLIENT, - }); - return context.with(setSpan(context.active(), span), () => - plugin._makeGrpcClientRemoteCall( - original, - args, - metadata, - this, - plugin - )(span) - ); - }; - }; - } - - /** - * This method handles the client remote call - */ - private _makeGrpcClientRemoteCall( - original: GrpcClientFunc, - args: any[], - metadata: grpcTypes.Metadata, - self: grpcTypes.Client, - plugin: GrpcPlugin - ) { - /** - * Patches a callback so that the current span for this trace is also ended - * when the callback is invoked. - */ - function patchedCallback( - span: Span, - callback: SendUnaryDataCallback, - _metadata: grpcTypes.Metadata - ) { - const wrappedFn = (err: grpcTypes.ServiceError, res: any) => { - if (err) { - if (err.code) { - span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - err.code.toString() - ); - } - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - plugin._moduleExports.status.OK.toString() - ); - } - - span.end(); - callback(err, res); - }; - return context.bind(wrappedFn); - } - - return (span: Span) => { - if (!span) { - return original.apply(self, args); - } - - // if unary or clientStream - if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, arg => { - return typeof arg === 'function'; - }); - if (callbackFuncIndex !== -1) { - args[callbackFuncIndex] = patchedCallback( - span, - args[callbackFuncIndex], - metadata - ); - } - } - - span.addEvent('sent'); - span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, - }); - - this._setSpanContext(metadata); - const call = original.apply(self, args); - - // if server stream or bidi - if (original.responseStream) { - // Both error and status events can be emitted - // the first one emitted set spanEnded to true - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - span.end(); - spanEnded = true; - } - }; - context.bind(call); - ((call as unknown) as events.EventEmitter).on( - 'error', - (err: grpcTypes.ServiceError) => { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - } - ); - - ((call as unknown) as events.EventEmitter).on( - 'status', - (status: SpanStatus) => { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, - status.code.toString() - ); - endSpan(); - } - ); - } - return call; - }; - } - - private _getMetadata( - original: GrpcClientFunc, - args: any[] - ): grpcTypes.Metadata { - let metadata: grpcTypes.Metadata; - - // This finds an instance of Metadata among the arguments. - // A possible issue that could occur is if the 'options' parameter from - // the user contains an '_internal_repr' as well as a 'getMap' function, - // but this is an extremely rare case. - let metadataIndex = findIndex(args, (arg: any) => { - return ( - arg && - typeof arg === 'object' && - arg._internal_repr && - typeof arg.getMap === 'function' - ); - }); - if (metadataIndex === -1) { - metadata = new this._moduleExports.Metadata(); - if (!original.requestStream) { - // unary or server stream - if (args.length === 0) { - // No argument (for the gRPC call) was provided, so we will have to - // provide one, since metadata cannot be the first argument. - // The internal representation of argument defaults to undefined - // in its non-presence. - // Note that we can't pass null instead of undefined because the - // serializer within gRPC doesn't accept it. - args.push(undefined); - } - metadataIndex = 1; - } else { - // client stream or bidi - metadataIndex = 0; - } - args.splice(metadataIndex, 0, metadata); - } else { - metadata = args[metadataIndex]; - } - return metadata; - } -} - -const basedir = path.dirname(require.resolve('grpc')); -const version = require(path.join(basedir, 'package.json')).version; -export const plugin = new GrpcPlugin(GrpcPlugin.component, version); diff --git a/packages/opentelemetry-plugin-grpc/src/index.ts b/packages/opentelemetry-plugin-grpc/src/index.ts deleted file mode 100644 index 4ffcf69671..0000000000 --- a/packages/opentelemetry-plugin-grpc/src/index.ts +++ /dev/null @@ -1,17 +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. - */ - -export * from './grpc'; diff --git a/packages/opentelemetry-plugin-grpc/src/types.ts b/packages/opentelemetry-plugin-grpc/src/types.ts deleted file mode 100644 index d85baab944..0000000000 --- a/packages/opentelemetry-plugin-grpc/src/types.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as grpcModule from 'grpc'; -import * as events from 'events'; -import { PluginConfig } from '@opentelemetry/core'; - -export type grpc = typeof grpcModule; - -export type IgnoreMatcher = string | RegExp | ((str: string) => boolean); - -export type SendUnaryDataCallback = ( - error: grpcModule.ServiceError | null, - value?: any, - trailer?: grpcModule.Metadata, - flags?: grpcModule.writeFlags -) => void; - -export interface GrpcPluginOptions extends PluginConfig { - /* Omits tracing on any gRPC methods that match any of - * the IgnoreMatchers in the ignoreGrpcMethods list - */ - ignoreGrpcMethods?: IgnoreMatcher[]; -} - -interface GrpcStatus { - code: number; - details: string; - metadata: grpcModule.Metadata; -} - -export type ServerCall = - | typeof grpcModule.ServerUnaryCall - | typeof grpcModule.ServerReadableStream - | typeof grpcModule.ServerWritableStream - | typeof grpcModule.ServerDuplexStream; - -export type ServerCallWithMeta = ServerCall & { - metadata: grpcModule.Metadata; - status: GrpcStatus; - request?: unknown; -} & events.EventEmitter; - -export type GrpcClientFunc = typeof Function & { - path: string; - requestStream: boolean; - responseStream: boolean; -}; - -export type GrpcInternalClientTypes = { - makeClientConstructor: typeof grpcModule.makeGenericClientConstructor; -}; - -// TODO: Delete if moving internal file loaders to BasePlugin -/** - * Maps a name (key) representing a internal file module and its exports - */ -export interface ModuleNameToFilePath { - client: string; // path/to/file - [wildcard: string]: string; // string indexer -} - -/** - * Maps a semver to a module:filepath Map - */ -export interface ModuleExportsMapping { - [semver: string]: ModuleNameToFilePath; -} diff --git a/packages/opentelemetry-plugin-grpc/src/utils.ts b/packages/opentelemetry-plugin-grpc/src/utils.ts deleted file mode 100644 index 4b7def379a..0000000000 --- a/packages/opentelemetry-plugin-grpc/src/utils.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanStatusCode, SpanStatus } from '@opentelemetry/api'; -import * as grpcTypes from 'grpc'; // For types only -import { IgnoreMatcher } from './types'; - -// Equivalent to lodash _.findIndex -export const findIndex: (args: T[], fn: (arg: T) => boolean) => number = ( - args, - fn: Function -) => { - let index = -1; - for (const arg of args) { - index++; - if (fn(arg)) { - return index; - } - } - return -1; -}; - -/** - * Convert a grpc status code to an opentelemetry SpanStatus code. - * @param status - */ -export const _grpcStatusCodeToOpenTelemetryStatusCode = ( - status?: grpcTypes.status -): SpanStatusCode => { - if (status !== undefined && status === 0) { - return SpanStatusCode.OK; - } - return SpanStatusCode.ERROR; -}; - -export const _grpcStatusCodeToSpanStatus = (status: number): SpanStatus => { - return { code: _grpcStatusCodeToOpenTelemetryStatusCode(status) }; -}; - -/** - * Returns true if methodName matches pattern - * @param methodName the name of the method - * @param pattern Match pattern - */ -const _satisfiesPattern = ( - methodName: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern.toLowerCase() === methodName.toLowerCase(); - } else if (pattern instanceof RegExp) { - return pattern.test(methodName); - } else if (typeof pattern === 'function') { - return pattern(methodName); - } else { - return false; - } -}; - -/** - * Returns true if the current plugin configuration - * ignores the given method. - * @param methodName the name of the method - * @param ignoredMethods a list of matching patterns - * @param onException an error handler for matching exceptions - */ -export const _methodIsIgnored = ( - methodName: string, - ignoredMethods?: IgnoreMatcher[] -): boolean => { - if (!ignoredMethods) { - // No ignored gRPC methods - return false; - } - - for (const pattern of ignoredMethods) { - if (_satisfiesPattern(methodName, pattern)) { - return true; - } - } - - return false; -}; diff --git a/packages/opentelemetry-plugin-grpc/src/version.ts b/packages/opentelemetry-plugin-grpc/src/version.ts deleted file mode 100644 index 6fdb996caa..0000000000 --- a/packages/opentelemetry-plugin-grpc/src/version.ts +++ /dev/null @@ -1,18 +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. - */ - -// this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; diff --git a/packages/opentelemetry-plugin-grpc/test/grpc.test.ts b/packages/opentelemetry-plugin-grpc/test/grpc.test.ts deleted file mode 100644 index a4ff260c2a..0000000000 --- a/packages/opentelemetry-plugin-grpc/test/grpc.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { runTests } from '@opentelemetry/grpc-utils/test'; -import { plugin, GrpcPlugin } from '../src/grpc'; -import * as grpc from 'grpc'; - -describe(`#${GrpcPlugin.component}`, () => { - runTests(plugin, GrpcPlugin.component, grpc, 12345); -}); diff --git a/packages/opentelemetry-plugin-grpc/tsconfig.json b/packages/opentelemetry-plugin-grpc/tsconfig.json deleted file mode 100644 index fed694f718..0000000000 --- a/packages/opentelemetry-plugin-grpc/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "build" - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "references": [ - { - "path": "../opentelemetry-context-async-hooks" - }, - { - "path": "../opentelemetry-core" - }, - { - "path": "../opentelemetry-grpc-utils" - }, - { - "path": "../opentelemetry-node" - }, - { - "path": "../opentelemetry-semantic-conventions" - }, - { - "path": "../opentelemetry-tracing" - } - ] -} diff --git a/packages/opentelemetry-plugin-http/.eslintignore b/packages/opentelemetry-plugin-http/.eslintignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/packages/opentelemetry-plugin-http/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/opentelemetry-plugin-http/.eslintrc.js b/packages/opentelemetry-plugin-http/.eslintrc.js deleted file mode 100644 index f726f3becb..0000000000 --- a/packages/opentelemetry-plugin-http/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - "env": { - "mocha": true, - "node": true - }, - ...require('../../eslint.config.js') -} diff --git a/packages/opentelemetry-plugin-http/.npmignore b/packages/opentelemetry-plugin-http/.npmignore deleted file mode 100644 index 9505ba9450..0000000000 --- a/packages/opentelemetry-plugin-http/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/coverage -/doc -/test diff --git a/packages/opentelemetry-plugin-http/LICENSE b/packages/opentelemetry-plugin-http/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/packages/opentelemetry-plugin-http/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/packages/opentelemetry-plugin-http/README.md b/packages/opentelemetry-plugin-http/README.md deleted file mode 100644 index b43ae67b7b..0000000000 --- a/packages/opentelemetry-plugin-http/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# OpenTelemetry HTTP Instrumentation for Node.js - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![devDependencies][devDependencies-image]][devDependencies-url] -[![Apache License][license-image]][license-image] - -This module provides automatic instrumentation for [`http`](https://nodejs.org/api/http.html). - -For automatic instrumentation see the -[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node) package. - -## Installation - -```bash -npm install --save @opentelemetry/plugin-http -``` - -## Usage - -OpenTelemetry HTTP 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 plugin (HTTP in this case), specify it in the Node Tracer's configuration. - -```js -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider({ - plugins: { - http: { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-http', - // http plugin options - } - } -}); -``` - -To load all of the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. - -```js -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider(); -``` - -See [examples/http](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/http) for a short example. - -### Http Plugin Options - -Http plugin has few options available to choose from. You can set the following: - -| Options | Type | Description | -| ------- | ---- | ----------- | -| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes | -| [`requestHook`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L60) | `HttpRequestCustomAttributeFunction` | Function for adding custom attributes before request is handled | -| [`responseHook`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L67) | `HttpResponseCustomAttributeFunction` | Function for adding custom attributes before response is handled | -| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths | -| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls | -| [`serverName`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L28) | `string` | The primary server name of the matched virtual host. | -| `requireParentforOutgoingSpans` | Boolean | Require that is a parent span to create new span for outgoing requests. | -| `requireParentforIncomingSpans` | Boolean | Require that is a parent span to create new span for incoming requests. | - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-http -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-http -[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-http&type=dev -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-http&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-http -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-http.svg diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts deleted file mode 100644 index 37c50a38a6..0000000000 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - SpanStatusCode, - context, - propagation, - Span, - SpanKind, - SpanOptions, - SpanStatus, - setSpan, - ROOT_CONTEXT, - getSpan, - suppressInstrumentation, - NOOP_TRACER, - diag, -} from '@opentelemetry/api'; -import { BasePlugin } from '@opentelemetry/core'; -import type { - ClientRequest, - IncomingMessage, - RequestOptions, - ServerResponse, -} from 'http'; -import { Socket } from 'net'; -import * as semver from 'semver'; -import * as shimmer from 'shimmer'; -import * as url from 'url'; -import { - Err, - Func, - Http, - HttpPluginConfig, - HttpRequestArgs, - ParsedRequestOptions, - ResponseEndArgs, -} from './types'; -import * as utils from './utils'; -import { VERSION } from './version'; - -/** - * Http instrumentation plugin for Opentelemetry - */ -export class HttpPlugin extends BasePlugin { - readonly component: string; - protected _config!: HttpPluginConfig; - /** keep track on spans not ended */ - private readonly _spanNotEnded: WeakSet; - - constructor(readonly moduleName: string, readonly version: string) { - super(`@opentelemetry/plugin-${moduleName}`, VERSION); - // For now component is equal to moduleName but it can change in the future. - this.component = this.moduleName; - this._spanNotEnded = new WeakSet(); - this._config = {}; - } - - /** Patches HTTP incoming and outcoming request functions. */ - protected patch() { - diag.debug('applying patch to %s@%s', this.moduleName, this.version); - - shimmer.wrap( - this._moduleExports, - 'request', - this._getPatchOutgoingRequestFunction() - ); - - // In Node >=8, http.get calls a private request method, therefore we patch it - // here too. - if (semver.satisfies(this.version, '>=8.0.0')) { - shimmer.wrap( - this._moduleExports, - 'get', - this._getPatchOutgoingGetFunction(this._moduleExports.request) - ); - } - - if ( - this._moduleExports && - this._moduleExports.Server && - this._moduleExports.Server.prototype - ) { - shimmer.wrap( - this._moduleExports.Server.prototype, - 'emit', - this._getPatchIncomingRequestFunction() - ); - } else { - diag.error( - 'Could not apply patch to %s.emit. Interface is not as expected.', - this.moduleName - ); - } - - return this._moduleExports; - } - - /** Unpatches all HTTP patched function. */ - protected unpatch(): void { - shimmer.unwrap(this._moduleExports, 'request'); - if (semver.satisfies(this.version, '>=8.0.0')) { - shimmer.unwrap(this._moduleExports, 'get'); - } - if ( - this._moduleExports && - this._moduleExports.Server && - this._moduleExports.Server.prototype - ) { - shimmer.unwrap(this._moduleExports.Server.prototype, 'emit'); - } - } - - /** - * Creates spans for incoming requests, restoring spans' context if applied. - */ - protected _getPatchIncomingRequestFunction() { - return (original: (event: string, ...args: unknown[]) => boolean) => { - return this._incomingRequestFunction(original); - }; - } - - /** - * Creates spans for outgoing requests, sending spans' context for distributed - * tracing. - */ - protected _getPatchOutgoingRequestFunction() { - return (original: Func): Func => { - return this._outgoingRequestFunction(original); - }; - } - - protected _getPatchOutgoingGetFunction( - clientRequest: ( - options: RequestOptions | string | url.URL, - ...args: HttpRequestArgs - ) => ClientRequest - ) { - return (_original: Func): Func => { - // Re-implement http.get. This needs to be done (instead of using - // getPatchOutgoingRequestFunction to patch it) because we need to - // set the trace context header before the returned ClientRequest is - // ended. The Node.js docs state that the only differences between - // request and get are that (1) get defaults to the HTTP GET method and - // (2) the returned request object is ended immediately. The former is - // already true (at least in supported Node versions up to v10), so we - // simply follow the latter. Ref: - // https://nodejs.org/dist/latest/docs/api/http.html#http_http_get_options_callback - // https://github.com/googleapis/cloud-trace-nodejs/blob/master/src/plugins/plugin-http.ts#L198 - return function outgoingGetRequest< - T extends RequestOptions | string | url.URL - >(options: T, ...args: HttpRequestArgs): ClientRequest { - const req = clientRequest(options, ...args); - req.end(); - return req; - }; - }; - } - - /** - * Attach event listeners to a client request to end span and add span attributes. - * - * @param request The original request object. - * @param options The arguments to the original function. - * @param span representing the current operation - */ - private _traceClientRequest( - request: ClientRequest, - options: ParsedRequestOptions, - span: Span - ): ClientRequest { - const hostname = - options.hostname || - options.host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; - const attributes = utils.getOutgoingRequestAttributes(options, { - component: this.component, - hostname, - }); - span.setAttributes(attributes); - if (this._config.requestHook) { - this._callRequestHook(span, request); - } - - request.on( - 'response', - (response: IncomingMessage & { aborted?: boolean }) => { - const responseAttributes = utils.getOutgoingRequestAttributesOnResponse( - response, - { hostname } - ); - span.setAttributes(responseAttributes); - if (this._config.responseHook) { - this._callResponseHook(span, response); - } - - context.bind(response); - diag.debug('outgoingRequest on response()'); - response.on('end', () => { - diag.debug('outgoingRequest on end()'); - let status: SpanStatus; - - if (response.aborted && !response.complete) { - status = { code: SpanStatusCode.ERROR }; - } else { - status = utils.parseResponseStatus(response.statusCode!); - } - - span.setStatus(status); - - if (this._config.applyCustomAttributesOnSpan) { - this._safeExecute( - span, - () => - this._config.applyCustomAttributesOnSpan!( - span, - request, - response - ), - false - ); - } - - this._closeHttpSpan(span); - }); - response.on('error', (error: Err) => { - utils.setSpanWithError(span, error, response); - this._closeHttpSpan(span); - }); - } - ); - request.on('close', () => { - if (!request.aborted) { - this._closeHttpSpan(span); - } - }); - request.on('error', (error: Err) => { - utils.setSpanWithError(span, error, request); - this._closeHttpSpan(span); - }); - - diag.debug('_traceClientRequest return request'); - return request; - } - - private _incomingRequestFunction( - original: (event: string, ...args: unknown[]) => boolean - ) { - const plugin = this; - return function incomingRequest( - this: {}, - event: string, - ...args: unknown[] - ): boolean { - // Only traces request events - if (event !== 'request') { - return original.apply(this, [event, ...args]); - } - - const request = args[0] as IncomingMessage; - const response = args[1] as ServerResponse & { socket: Socket }; - const pathname = request.url - ? url.parse(request.url).pathname || '/' - : '/'; - const method = request.method || 'GET'; - - diag.debug('%s plugin incomingRequest', plugin.moduleName); - - if ( - utils.isIgnored( - pathname, - plugin._config.ignoreIncomingPaths, - (e: Error) => diag.error('caught ignoreIncomingPaths error: ', e) - ) - ) { - return context.with(suppressInstrumentation(context.active()), () => { - context.bind(request); - context.bind(response); - return original.apply(this, [event, ...args]); - }); - } - - const headers = request.headers; - - const spanOptions: SpanOptions = { - kind: SpanKind.SERVER, - attributes: utils.getIncomingRequestAttributes(request, { - component: plugin.component, - serverName: plugin._config.serverName, - }), - }; - - return context.with(propagation.extract(ROOT_CONTEXT, headers), () => { - const span = plugin._startHttpSpan(`HTTP ${method}`, spanOptions); - - return context.with(setSpan(context.active(), span), () => { - context.bind(request); - context.bind(response); - - if (plugin._config.requestHook) { - plugin._callRequestHook(span, request); - } - if (plugin._config.responseHook) { - plugin._callResponseHook(span, response); - } - - // Wraps end (inspired by: - // https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75) - const originalEnd = response.end; - response.end = function ( - this: ServerResponse, - ..._args: ResponseEndArgs - ) { - response.end = originalEnd; - // Cannot pass args of type ResponseEndArgs, - const returned = plugin._safeExecute( - span, - () => response.end.apply(this, arguments as any), - true - ); - - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); - - span - .setAttributes(attributes) - .setStatus(utils.parseResponseStatus(response.statusCode)); - - if (plugin._config.applyCustomAttributesOnSpan) { - plugin._safeExecute( - span, - () => - plugin._config.applyCustomAttributesOnSpan!( - span, - request, - response - ), - false - ); - } - - plugin._closeHttpSpan(span); - return returned; - }; - - return plugin._safeExecute( - span, - () => original.apply(this, [event, ...args]), - true - ); - }); - }); - }; - } - - private _outgoingRequestFunction( - original: Func - ): Func { - const plugin = this; - return function outgoingRequest( - this: {}, - options: url.URL | RequestOptions | string, - ...args: unknown[] - ): ClientRequest { - if (!utils.isValidOptionsType(options)) { - return original.apply(this, [options, ...args]); - } - - const extraOptions = - typeof args[0] === 'object' && - (typeof options === 'string' || options instanceof url.URL) - ? (args.shift() as RequestOptions) - : undefined; - const { origin, pathname, method, optionsParsed } = utils.getRequestInfo( - options, - extraOptions - ); - - if ( - utils.isIgnored( - origin + pathname, - plugin._config.ignoreOutgoingUrls, - (e: Error) => diag.error('caught ignoreOutgoingUrls error: ', e) - ) - ) { - return original.apply(this, [optionsParsed, ...args]); - } - - const operationName = `HTTP ${method}`; - const spanOptions: SpanOptions = { - kind: SpanKind.CLIENT, - }; - const span = plugin._startHttpSpan(operationName, spanOptions); - if (!optionsParsed.headers) { - optionsParsed.headers = {}; - } - propagation.inject( - setSpan(context.active(), span), - optionsParsed.headers - ); - - const request: ClientRequest = plugin._safeExecute( - span, - () => original.apply(this, [optionsParsed, ...args]), - true - ); - - diag.debug('%s plugin outgoingRequest', plugin.moduleName); - context.bind(request); - return plugin._traceClientRequest(request, optionsParsed, span); - }; - } - - private _startHttpSpan(name: string, options: SpanOptions) { - /* - * If a parent is required but not present, we use a `NoopSpan` to still - * propagate context without recording it. - */ - const requireParent = - options.kind === SpanKind.CLIENT - ? this._config.requireParentforOutgoingSpans - : this._config.requireParentforIncomingSpans; - - let span: Span; - const currentSpan = getSpan(context.active()); - - if (requireParent === true && currentSpan === undefined) { - // TODO: Refactor this when a solution is found in - // https://github.com/open-telemetry/opentelemetry-specification/issues/530 - span = NOOP_TRACER.startSpan(name, options); - } else if (requireParent === true && currentSpan?.context().isRemote) { - span = currentSpan; - } else { - span = this._tracer.startSpan(name, options); - } - this._spanNotEnded.add(span); - return span; - } - - private _closeHttpSpan(span: Span) { - if (!this._spanNotEnded.has(span)) { - return; - } - - span.end(); - this._spanNotEnded.delete(span); - } - - private _callResponseHook( - span: Span, - response: IncomingMessage | ServerResponse - ) { - this._safeExecute( - span, - () => this._config.responseHook!(span, response), - false - ); - } - - private _callRequestHook( - span: Span, - request: ClientRequest | IncomingMessage - ) { - this._safeExecute( - span, - () => this._config.requestHook!(span, request), - false - ); - } - - private _safeExecute< - T extends (...args: unknown[]) => ReturnType, - K extends boolean - >( - span: Span, - execute: T, - rethrow: K - ): K extends true ? ReturnType : ReturnType | void; - private _safeExecute ReturnType>( - span: Span, - execute: T, - rethrow: boolean - ): ReturnType | void { - try { - return execute(); - } catch (error) { - if (rethrow) { - utils.setSpanWithError(span, error); - this._closeHttpSpan(span); - throw error; - } - diag.error('caught error ', error); - } - } -} - -export const plugin = new HttpPlugin('http', process.versions.node); diff --git a/packages/opentelemetry-plugin-http/src/index.ts b/packages/opentelemetry-plugin-http/src/index.ts deleted file mode 100644 index 265bc235a7..0000000000 --- a/packages/opentelemetry-plugin-http/src/index.ts +++ /dev/null @@ -1,19 +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. - */ - -export * from './http'; -export * from './types'; -export * from './utils'; diff --git a/packages/opentelemetry-plugin-http/src/types.ts b/packages/opentelemetry-plugin-http/src/types.ts deleted file mode 100644 index 4e9d456237..0000000000 --- a/packages/opentelemetry-plugin-http/src/types.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Span } from '@opentelemetry/api'; -import { PluginConfig } from '@opentelemetry/core'; -import type * as http from 'http'; -import { - ClientRequest, - get, - IncomingMessage, - request, - ServerResponse, -} from 'http'; -import * as url from 'url'; - -export type IgnoreMatcher = string | RegExp | ((url: string) => boolean); -export type HttpCallback = (res: IncomingMessage) => void; -export type RequestFunction = typeof request; -export type GetFunction = typeof get; - -export type HttpCallbackOptional = HttpCallback | undefined; - -// from node 10+ -export type RequestSignature = [http.RequestOptions, HttpCallbackOptional] & - HttpCallback; - -export type HttpRequestArgs = Array; - -export type ParsedRequestOptions = - | (http.RequestOptions & Partial) - | http.RequestOptions; -export type Http = typeof http; -/* tslint:disable-next-line:no-any */ -export type Func = (...args: any[]) => T; -export type ResponseEndArgs = - | [((() => void) | undefined)?] - | [unknown, ((() => void) | undefined)?] - | [unknown, string, ((() => void) | undefined)?]; - -export interface HttpCustomAttributeFunction { - ( - span: Span, - request: ClientRequest | IncomingMessage, - response: IncomingMessage | ServerResponse - ): void; -} - -export interface HttpRequestCustomAttributeFunction { - (span: Span, request: ClientRequest | IncomingMessage): void; -} - -export interface HttpResponseCustomAttributeFunction { - (span: Span, response: IncomingMessage | ServerResponse): void; -} - -/** - * Options available for the HTTP Plugin (see [documentation](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-http#http-plugin-options)) - */ -export interface HttpPluginConfig extends PluginConfig { - /** Not trace all incoming requests that match paths */ - ignoreIncomingPaths?: IgnoreMatcher[]; - /** Not trace all outgoing requests that match urls */ - ignoreOutgoingUrls?: IgnoreMatcher[]; - /** Function for adding custom attributes after response is handled */ - applyCustomAttributesOnSpan?: HttpCustomAttributeFunction; - /** Function for adding custom attributes before request is handled */ - requestHook?: HttpRequestCustomAttributeFunction; - /** Function for adding custom attributes before response is handled */ - responseHook?: HttpResponseCustomAttributeFunction; - /** The primary server name of the matched virtual host. */ - serverName?: string; - /** Require parent to create span for outgoing requests */ - requireParentforOutgoingSpans?: boolean; - /** Require parent to create span for incoming requests */ - requireParentforIncomingSpans?: boolean; -} - -export interface Err extends Error { - errno?: number; - code?: string; - path?: string; - syscall?: string; - stack?: string; -} diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts deleted file mode 100644 index e23b5aeb0f..0000000000 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - SpanAttributes, - SpanStatusCode, - Span, - SpanStatus, -} from '@opentelemetry/api'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; -import { - ClientRequest, - IncomingHttpHeaders, - IncomingMessage, - OutgoingHttpHeaders, - RequestOptions, - ServerResponse, -} from 'http'; -import * as url from 'url'; -import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; - -/** - * Get an absolute url - */ -export const getAbsoluteUrl = ( - requestUrl: ParsedRequestOptions | null, - headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = 'http:' -): string => { - const reqUrlObject = requestUrl || {}; - const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || '').toString(); - const path = reqUrlObject.path || '/'; - let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; - - // if there is no port in host and there is a port - // it should be displayed if it's not 80 and 443 (default ports) - if ( - (host as string).indexOf(':') === -1 && - port && - port !== '80' && - port !== '443' - ) { - host += `:${port}`; - } - - return `${protocol}//${host}${path}`; -}; -/** - * Parse status code from HTTP response. [More details](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#status) - */ -export const parseResponseStatus = ( - statusCode: number -): Omit => { - // 1xx, 2xx, 3xx are OK - if (statusCode >= 100 && statusCode < 400) { - return { code: SpanStatusCode.OK }; - } - - // All other codes are error - return { code: SpanStatusCode.ERROR }; -}; - -/** - * Returns whether the Expect header is on the given options object. - * @param options Options for http.request. - */ -export const hasExpectHeader = (options: RequestOptions): boolean => { - if (!options.headers) { - return false; - } - - const keys = Object.keys(options.headers); - return !!keys.find(key => key.toLowerCase() === 'expect'); -}; - -/** - * Check whether the given obj match pattern - * @param constant e.g URL of request - * @param pattern Match pattern - */ -export const satisfiesPattern = ( - constant: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern === constant; - } else if (pattern instanceof RegExp) { - return pattern.test(constant); - } else if (typeof pattern === 'function') { - return pattern(constant); - } else { - throw new TypeError('Pattern is in unsupported datatype'); - } -}; - -/** - * Check whether the given request is ignored by configuration - * It will not re-throw exceptions from `list` provided by the client - * @param constant e.g URL of request - * @param [list] List of ignore patterns - * @param [onException] callback for doing something when an exception has - * occurred - */ -export const isIgnored = ( - constant: string, - list?: IgnoreMatcher[], - onException?: (error: Error) => void -): boolean => { - if (!list) { - // No ignored urls - trace everything - return false; - } - // Try/catch outside the loop for failing fast - try { - for (const pattern of list) { - if (satisfiesPattern(constant, pattern)) { - return true; - } - } - } catch (e) { - if (onException) { - onException(e); - } - } - - return false; -}; - -/** - * Sets the span with the error passed in params - * @param {Span} span the span that need to be set - * @param {Error} error error that will be set to span - * @param {(IncomingMessage | ClientRequest)} [obj] used for enriching the status by checking the statusCode. - */ -export const setSpanWithError = ( - span: Span, - error: Err, - obj?: IncomingMessage | ClientRequest -) => { - const message = error.message; - - span.setAttributes({ - [HttpAttribute.HTTP_ERROR_NAME]: error.name, - [HttpAttribute.HTTP_ERROR_MESSAGE]: message, - }); - - if (!obj) { - span.setStatus({ code: SpanStatusCode.ERROR, message }); - return; - } - - let status: SpanStatus; - if ((obj as IncomingMessage).statusCode) { - status = parseResponseStatus((obj as IncomingMessage).statusCode!); - } else if ((obj as ClientRequest).aborted) { - status = { code: SpanStatusCode.ERROR }; - } else { - status = { code: SpanStatusCode.ERROR }; - } - - status.message = message; - - span.setStatus(status); -}; - -/** - * Adds attributes for request content-length and content-encoding HTTP headers - * @param { IncomingMessage } Request object whose headers will be analyzed - * @param { SpanAttributes } SpanAttributes object to be modified - */ -export const setRequestContentLengthAttribute = ( - request: IncomingMessage, - attributes: SpanAttributes -) => { - const length = getContentLength(request.headers); - if (length === null) return; - - if (isCompressed(request.headers)) { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; - } else { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED] = length; - } -}; - -/** - * Adds attributes for response content-length and content-encoding HTTP headers - * @param { IncomingMessage } Response object whose headers will be analyzed - * @param { SpanAttributes } SpanAttributes object to be modified - */ -export const setResponseContentLengthAttribute = ( - response: IncomingMessage, - attributes: SpanAttributes -) => { - const length = getContentLength(response.headers); - if (length === null) return; - - if (isCompressed(response.headers)) { - attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; - } else { - attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED - ] = length; - } -}; - -function getContentLength( - headers: OutgoingHttpHeaders | IncomingHttpHeaders -): number | null { - const contentLengthHeader = headers['content-length']; - if (contentLengthHeader === undefined) return null; - - const contentLength = parseInt(contentLengthHeader as string, 10); - if (isNaN(contentLength)) return null; - - return contentLength; -} - -export const isCompressed = ( - headers: OutgoingHttpHeaders | IncomingHttpHeaders -): boolean => { - const encoding = headers['content-encoding']; - - return !!encoding && encoding !== 'identity'; -}; - -/** - * Makes sure options is an url object - * return an object with default value and parsed options - * @param options original options for the request - * @param [extraOptions] additional options for the request - */ -export const getRequestInfo = ( - options: url.URL | RequestOptions | string, - extraOptions?: RequestOptions -) => { - let pathname = '/'; - let origin = ''; - let optionsParsed: RequestOptions; - if (typeof options === 'string') { - optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; - origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; - if (extraOptions !== undefined) { - Object.assign(optionsParsed, extraOptions); - } - } else if (options instanceof url.URL) { - optionsParsed = { - protocol: options.protocol, - hostname: - typeof options.hostname === 'string' && options.hostname.startsWith('[') - ? options.hostname.slice(1, -1) - : options.hostname, - path: `${options.pathname || ''}${options.search || ''}`, - }; - if (options.port !== '') { - optionsParsed.port = Number(options.port); - } - if (options.username || options.password) { - optionsParsed.auth = `${options.username}:${options.password}`; - } - pathname = options.pathname; - origin = options.origin; - if (extraOptions !== undefined) { - Object.assign(optionsParsed, extraOptions); - } - } else { - optionsParsed = Object.assign({}, options); - pathname = (options as url.URL).pathname; - if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || '/'; - } - origin = `${optionsParsed.protocol || 'http:'}//${ - optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` - }`; - } - - if (hasExpectHeader(optionsParsed)) { - optionsParsed.headers = Object.assign({}, optionsParsed.headers); - } else if (!optionsParsed.headers) { - optionsParsed.headers = {}; - } - // some packages return method in lowercase.. - // ensure upperCase for consistency - const method = optionsParsed.method - ? optionsParsed.method.toUpperCase() - : 'GET'; - - return { origin, pathname, method, optionsParsed }; -}; - -/** - * Makes sure options is of type string or object - * @param options for the request - */ -export const isValidOptionsType = (options: unknown): boolean => { - if (!options) { - return false; - } - - const type = typeof options; - return type === 'string' || (type === 'object' && !Array.isArray(options)); -}; - -/** - * Returns outgoing request attributes scoped to the options passed to the request - * @param {ParsedRequestOptions} requestOptions the same options used to make the request - * @param {{ component: string, hostname: string }} options used to pass data needed to create attributes - */ -export const getOutgoingRequestAttributes = ( - requestOptions: ParsedRequestOptions, - options: { component: string; hostname: string } -): SpanAttributes => { - const host = requestOptions.host; - const hostname = - requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; - const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; - const headers = requestOptions.headers || {}; - const userAgent = headers['user-agent']; - const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( - requestOptions, - headers, - `${options.component}:` - ), - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_TARGET]: requestOptions.path || '/', - [GeneralAttribute.NET_PEER_NAME]: hostname, - }; - - if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; - } - return attributes; -}; - -/** - * Returns attributes related to the kind of HTTP protocol used - * @param {string} [kind] Kind of HTTP protocol used: "1.0", "1.1", "2", "SPDY" or "QUIC". - */ -export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { - const attributes: SpanAttributes = {}; - if (kind) { - attributes[HttpAttribute.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== 'QUIC') { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_TCP; - } else { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_UDP; - } - } - return attributes; -}; - -/** - * Returns outgoing request attributes scoped to the response data - * @param {IncomingMessage} response the response object - * @param {{ hostname: string }} options used to pass data needed to create attributes - */ -export const getOutgoingRequestAttributesOnResponse = ( - response: IncomingMessage, - options: { hostname: string } -): SpanAttributes => { - const { statusCode, statusMessage, httpVersion, socket } = response; - const { remoteAddress, remotePort } = socket; - - const attributes: SpanAttributes = { - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, - }; - - setResponseContentLengthAttribute(response, attributes); - - if (statusCode) { - attributes[HttpAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[HttpAttribute.HTTP_STATUS_TEXT] = ( - statusMessage || '' - ).toUpperCase(); - } - - const httpKindAttributes = getAttributesFromHttpKind(httpVersion); - return Object.assign(attributes, httpKindAttributes); -}; - -/** - * Returns incoming request attributes scoped to the request data - * @param {IncomingMessage} request the request object - * @param {{ component: string, serverName?: string }} options used to pass data needed to create attributes - */ -export const getIncomingRequestAttributes = ( - request: IncomingMessage, - options: { component: string; serverName?: string } -): SpanAttributes => { - const headers = request.headers; - const userAgent = headers['user-agent']; - const ips = headers['x-forwarded-for']; - const method = request.method || 'GET'; - const httpVersion = request.httpVersion; - const requestUrl = request.url ? url.parse(request.url) : null; - const host = requestUrl?.host || headers.host; - const hostname = - requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; - const serverName = options.serverName; - const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( - requestUrl, - headers, - `${options.component}:` - ), - [HttpAttribute.HTTP_HOST]: host, - [GeneralAttribute.NET_HOST_NAME]: hostname, - [HttpAttribute.HTTP_METHOD]: method, - }; - - if (typeof ips === 'string') { - attributes[HttpAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; - } - - if (typeof serverName === 'string') { - attributes[HttpAttribute.HTTP_SERVER_NAME] = serverName; - } - - if (requestUrl) { - attributes[HttpAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[HttpAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; - } - - if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; - } - - setRequestContentLengthAttribute(request, attributes); - - const httpKindAttributes = getAttributesFromHttpKind(httpVersion); - return Object.assign(attributes, httpKindAttributes); -}; - -/** - * Returns incoming request attributes scoped to the response data - * @param {(ServerResponse & { socket: Socket; })} response the response object - */ -export const getIncomingRequestAttributesOnResponse = ( - request: IncomingMessage & { __ot_middlewares?: string[] }, - response: ServerResponse -): SpanAttributes => { - // use socket from the request, - // since it may be detached from the response object in keep-alive mode - const { socket } = request; - const { statusCode, statusMessage } = response; - const { localAddress, localPort, remoteAddress, remotePort } = socket; - const { __ot_middlewares } = (request as unknown) as { - [key: string]: unknown; - }; - const route = Array.isArray(__ot_middlewares) - ? __ot_middlewares - .filter(path => path !== '/') - .map(path => { - return path[0] === '/' ? path : '/' + path; - }) - .join('') - : undefined; - - const attributes: SpanAttributes = { - [GeneralAttribute.NET_HOST_IP]: localAddress, - [GeneralAttribute.NET_HOST_PORT]: localPort, - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_STATUS_CODE]: statusCode, - [HttpAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), - }; - - if (route !== undefined) { - attributes[HttpAttribute.HTTP_ROUTE] = route; - } - return attributes; -}; diff --git a/packages/opentelemetry-plugin-http/src/version.ts b/packages/opentelemetry-plugin-http/src/version.ts deleted file mode 100644 index 6fdb996caa..0000000000 --- a/packages/opentelemetry-plugin-http/src/version.ts +++ /dev/null @@ -1,18 +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. - */ - -// this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; diff --git a/packages/opentelemetry-plugin-http/test/fixtures/google.json b/packages/opentelemetry-plugin-http/test/fixtures/google.json deleted file mode 100644 index 62301ab56b..0000000000 --- a/packages/opentelemetry-plugin-http/test/fixtures/google.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "scope": "http://www.google.com", - "method": "GET", - "path": "/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8", - "body": "", - "status": 200, - "response": "3c21646f63747970652068746d6c3e3c68746d6c206c616e673d22656e2d4341223e3c686561643e3c6d65746120636861727365743d225554462d38223e3c6d65746120636f6e74656e743d222f696d616765732f6272616e64696e672f676f6f676c65672f31782f676f6f676c65675f7374616e646172645f636f6c6f725f31323864702e706e6722206974656d70726f703d22696d616765223e3c7469746c653e6178696f73202d20476f6f676c65205365617263683c2f7469746c653e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d77696e646f772e706572666f726d616e63653b77696e646f772e73746172743d286e65772044617465292e67657454696d6528293b613a7b76617220623d77696e646f773b69662861297b76617220633d612e74696d696e673b69662863297b76617220643d632e6e617669676174696f6e53746172742c653d632e726573706f6e736553746172743b696628653e642626653c3d77696e646f772e7374617274297b77696e646f772e73746172743d653b622e777372743d652d643b627265616b20617d7d612e6e6f77262628622e777372743d4d6174682e666c6f6f7228612e6e6f77282929297d7d77696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d3b676f6f676c652e6166743d66756e6374696f6e2866297b662e7365744174747269627574652822646174612d696d6c222c2b6e65772044617465297d3b7d292e63616c6c2874686973293b2866756e6374696f6e28297b76617220633d5b5d2c653d303b77696e646f772e70696e673d66756e6374696f6e2862297b2d313d3d622e696e6465784f662822267a782229262628622b3d22267a783d222b286e65772044617465292e67657454696d652829293b76617220613d6e657720496d6167652c643d652b2b3b635b645d3d613b612e6f6e6572726f723d612e6f6e6c6f61643d612e6f6e61626f72743d66756e6374696f6e28297b64656c65746520635b645d7d3b612e7372633d627d3b7d292e63616c6c2874686973293b3c2f7363726970743e3c7374796c653e626f64797b6d617267696e3a30206175746f3b6d61782d77696474683a37333670783b70616464696e673a30203870787d617b636f6c6f723a233139363744323b746578742d6465636f726174696f6e3a6e6f6e653b7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c2e31297d613a766973697465647b636f6c6f723a233442313141387d613a686f7665727b746578742d6465636f726174696f6e3a756e6465726c696e657d696d677b626f726465723a307d68746d6c7b666f6e742d66616d696c793a526f626f746f2c48656c7665746963614e6575652c417269616c2c73616e732d73657269663b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b746578742d73697a652d61646a7573743a313030253b636f6c6f723a233343343034333b776f72642d777261703a627265616b2d776f72643b6261636b67726f756e642d636f6c6f723a236666667d2e625273576e637b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d746f703a31707820736f6c696420236530653065303b6865696768743a333970783b6f766572666c6f773a68696464656e7d2e4e365257567b6865696768743a353170783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e5576363771627b626f782d7061636b3a6a7573746966793b666f6e742d73697a653a313270783b6c696e652d6865696768743a333770783b6a7573746966792d636f6e74656e743a73706163652d6265747765656e3b6a7573746966792d636f6e74656e743a73706163652d6265747765656e7d2e55763637716220612c2e557636377162207370616e7b636f6c6f723a233735373537353b646973706c61793a626c6f636b3b666c65783a6e6f6e653b70616464696e673a3020313670783b746578742d616c69676e3a63656e7465723b746578742d7472616e73666f726d3a7570706572636173653b7d7370616e2e4f585875707b626f726465722d626f74746f6d3a32707820736f6c696420233432383566343b636f6c6f723a233432383566343b666f6e742d7765696768743a626f6c647d612e655a743878643a766973697465647b636f6c6f723a233735373537357d2e46456c6273667b626f726465722d6c6566743a31707820736f6c6964207267626128302c302c302c2e3132297d6865616465722061727469636c657b6f766572666c6f773a76697369626c657d2e5067373062667b6865696768743a333970783b646973706c61793a626f783b646973706c61793a666c65783b646973706c61793a666c65783b77696474683a313030257d2e4830505165637b706f736974696f6e3a72656c61746976653b666c65783a317d2e7362637b646973706c61793a666c65783b77696474683a313030257d2e50673730626620696e7075747b6d617267696e3a3270782034707820327078203870783b7d2e787b77696474683a323670783b636f6c6f723a233735373537353b666f6e743a323770782f3338707820617269616c2c2073616e732d73657269663b6c696e652d6865696768743a343070783b7d237164436c77627b666c65783a302030206175746f3b77696474683a333970783b6865696768743a333970783b626f726465722d626f74746f6d3a303b70616464696e673a303b626f726465722d746f702d72696768742d7261646975733a3870783b6261636b67726f756e642d636f6c6f723a233362373865373b626f726465723a31707820736f6c696420233333363764363b6261636b67726f756e642d696d6167653a75726c28646174613a696d6167652f6769663b6261736536342c52306c474f4464684a41416a41504948414f44722f6e436b2b4d505a2f466d5639367a4b2b2f372b2f354b352b6b714c39697741414141414a41416a41454144616e693633503477796b6d624b635152584473635141454d586d6d65614c51564c43756b7a79433039416a66654b37762f4d41616a41434c68504d564167776a73556345695a61387867415972567176324b783269777349414141426b6e664242414b7254453449634d796f743875723864617471496251664a646e41666f3257453642563035775849694a69676b414f773d3d293b7d2e73637b666f6e742d73697a653a3b706f736974696f6e3a6162736f6c7574653b746f703a333970783b6c6566743a303b72696768743a303b626f782d736861646f773a3070782032707820357078207267626128302c302c302c302e32293b7a2d696e6465783a323b6261636b67726f756e642d636f6c6f723a236666667d2e73633e6469767b70616464696e673a3130707820313070783b70616464696e672d6c6566743a313670783b70616464696e672d6c6566743a313470783b626f726465722d746f703a31707820736f6c696420234446453145357d2e7363737b6261636b67726f756e642d636f6c6f723a236635663566353b7d2e6e6f484978637b646973706c61793a626c6f636b3b666f6e742d73697a653a313670783b70616464696e673a3020302030203870783b666c65783a313b6865696768743a333570783b6f75746c696e653a6e6f6e653b626f726465723a6e6f6e653b77696474683a313030253b2d7765626b69742d7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c30293b6f766572666c6f773a68696464656e3b7d2e73626320696e7075745b747970653d746578745d7b6261636b67726f756e643a6e6f6e657d2e736d6c202e634f6c3449647b646973706c61793a6e6f6e657d2e6c7b646973706c61793a6e6f6e657d2e736d6c206865616465727b6261636b67726f756e643a6e6f6e657d2e736d6c202e6c7b646973706c61793a626c6f636b3b70616464696e673a30203870787d2e736d6c202e6c7b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b626f726465722d7261646975733a3270782030203020303b666f6e743a323270782f33367078204675747572612c20417269616c2c2073616e732d73657269663b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e627a316c42627b6261636b67726f756e643a236666663b626f726465722d7261646975733a38707820387078203020303b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d746f703a313070787d2e4b50374c43627b626f726465722d7261646975733a30203020387078203870783b626f782d736861646f773a30203270782033707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d626f74746f6d3a313070783b6f766572666c6f773a68696464656e7d2e634f6c3449647b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b666f6e743a32327074204675747572612c20417269616c2c2073616e732d73657269663b70616464696e673a3130707820302035707820303b6865696768743a333770783b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e634f6c344964207370616e7b646973706c61793a696e6c696e652d626c6f636b7d2e533539316a7b6865696768743a313030257d2e5636677756647b636f6c6f723a233432383546347d2e69576b7576647b636f6c6f723a234541343333357d2e63447251377b636f6c6f723a236662636330357d2e6e746c52397b636f6c6f723a233334413835337d2e744a334d79637b2d7765626b69742d7472616e73666f726d3a726f74617465282d3230646567293b706f736974696f6e3a72656c61746976653b6c6566743a2d3170783b646973706c61793a696e6c696e652d626c6f636b7d666f6f7465727b746578742d616c69676e3a63656e7465723b6d617267696e2d746f703a313870787d666f6f74657220612c666f6f74657220613a766973697465642c2e736d695562627b636f6c6f723a233566363336387d2e6b73545534637b6d617267696e3a3020313370787d236d436c6a6f627b6d617267696e2d746f703a333670787d236d436c6a6f623e6469767b6d617267696e3a323070787d3c2f7374796c653e3c2f686561643e3c626f6479206a736d6f64656c3d2220223e3c6865616465722069643d22686472223e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220633d3530303b2866756e6374696f6e28297b77696e646f772e73637265656e262677696e646f772e73637265656e2e77696474683c3d63262677696e646f772e73637265656e2e6865696768743c3d632626646f63756d656e742e676574456c656d656e7442794964282268647222292e636c6173734c6973742e6164642822736d6c22293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c64697620636c6173733d22634f6c344964223e3c6120687265663d222f3f73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673514f776743223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c2f6469763e3c64697620636c6173733d22627a316c4262223e3c666f726d20636c6173733d22506737306266222069643d227366223e3c6120636c6173733d226c2220687265663d222f3f6f75747075743d73656172636826616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735150416745223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c696e707574206e616d653d226965222076616c75653d2249534f2d383835392d312220747970653d2268696464656e223e3c64697620636c6173733d22483050516563223e3c64697620636c6173733d227362632065736263223e3c696e70757420636c6173733d226e6f48497863222076616c75653d226178696f7322206175746f6361706974616c697a653d226e6f6e6522206175746f636f6d706c6574653d226f666622206e616d653d227122207370656c6c636865636b3d2266616c73652220747970653d2274657874223e3c696e707574206e616d653d226f712220747970653d2268696464656e223e3c696e707574206e616d653d226171732220747970653d2268696464656e223e3c64697620636c6173733d2278223ed73c2f6469763e3c64697620636c6173733d227363223e3c2f6469763e3c2f6469763e3c2f6469763e3c627574746f6e2069643d227164436c77622220747970653d227375626d6974223e3c2f627574746f6e3e3c2f666f726d3e3c2f6469763e3c6e6f7363726970743e3c6d65746120636f6e74656e743d22303b75726c3d2f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b505942512220687474702d65717569763d2272656672657368223e3c7374796c653e7461626c652c6469762c7370616e2c707b646973706c61793a6e6f6e657d3c2f7374796c653e3c646976207374796c653d22646973706c61793a626c6f636b223e506c6561736520636c69636b203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b50594251223e686572653c2f613e20696620796f7520617265206e6f7420726564697265637465642077697468696e206120666577207365636f6e64732e3c2f6469763e3c2f6e6f7363726970743e3c2f6865616465723e3c6469762069643d226d61696e223e3c6469763e3c64697620636c6173733d224b50374c4362223e203c64697620636c6173733d22625273576e63223e203c64697620636c6173733d224e36525756223e203c64697620636c6173733d2250673730626620557636377162223e203c7370616e20636c6173733d224f58587570223e416c6c3c2f7370616e3e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6e777326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943436742223e4e6577733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d76696426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943536743223e566964656f733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6973636826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943696744223e496d616765733c2f613e2020203c6120687265663d22687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d6178696f7326616d703b756d3d3126616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943796745223e4d6170733c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d73686f7026616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944436746223e53686f7070696e673c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d626b7326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944536747223e426f6f6b733c2f613e20202020203c64697620636c6173733d2246456c627366223e3c6120687265663d222f616476616e6365645f73656172636822207374796c653d2277686974652d73706163653a6e6f77726170222069643d2273742d746f67676c652220726f6c653d22627574746f6e223e53656172636820746f6f6c733c2f613e3c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e3c64697620636c6173733d22506737306266207745736a6264205a494e62626320787064204f396735636320755550476922207374796c653d22646973706c61793a6e6f6e65222069643d2273742d63617264223e3c7374796c653e2e7745736a62647b6261636b67726f756e642d636f6c6f723a236666663b6865696768743a343470783b77686974652d73706163653a6e6f777261707d2e636f505538637b6865696768743a363070783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e586a326175657b6865696768743a343470783b6f766572666c6f773a68696464656e7d2e526e4e477a657b6d617267696e3a3131707820313670787d2e7745736a6264206469762c2e7745736a626420612c2e7745736a6264206c697b6f75746c696e652d77696474683a303b6f75746c696e653a6e6f6e657d3c2f7374796c653e3c64697620636c6173733d22586a32617565223e3c64697620636c6173733d22636f50553863223e3c64697620636c6173733d22526e4e477a65223e3c7374796c653e2e5041394a357b646973706c61793a696e6c696e652d626c6f636b7d2e5258614f66647b646973706c61793a696e6c696e652d626c6f636b3b6865696768743a323270783b706f736974696f6e3a72656c61746976653b70616464696e672d746f703a303b70616464696e672d626f74746f6d3a303b70616464696e672d72696768743a313670783b70616464696e672d6c6566743a303b6c696e652d6865696768743a323270783b637572736f723a706f696e7465723b746578742d7472616e73666f726d3a7570706572636173653b666f6e742d73697a653a313270783b636f6c6f723a233735373537357d2e736131746f637b646973706c61793a6e6f6e653b706f736974696f6e3a6162736f6c7574653b6261636b67726f756e643a236666663b626f726465723a31707820736f6c696420236436643664363b626f782d736861646f773a302032707820347078207267626128302c302c302c302e33293b6d617267696e3a303b77686974652d73706163653a6e6f777261703b7a2d696e6465783a3130333b6c696e652d6865696768743a313770783b70616464696e672d746f703a3570783b70616464696e672d626f74746f6d3a3570783b70616464696e672d6c6566743a3070787d2e5041394a353a686f766572202e736131746f637b646973706c61793a626c6f636b7d2e6d475379386420613a6163746976652c2e5258614f66643a6163746976657b636f6c6f723a233432383566347d3c2f7374796c653e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c7374796c653e2e54574d4f55637b646973706c61793a696e6c696e652d626c6f636b3b70616464696e672d72696768743a313470783b77686974652d73706163653a6e6f777261707d2e7651597547667b666f6e742d7765696768743a626f6c647d2e4f6d54497a667b626f726465722d636f6c6f723a23393039303930207472616e73706172656e743b626f726465722d7374796c653a736f6c69643b626f726465722d77696474683a347078203470782030203470783b77696474683a303b6865696768743a303b6d617267696e2d6c6566743a2d313070783b746f703a3530253b6d617267696e2d746f703a2d3270783b706f736974696f6e3a6162736f6c7574657d2e5258614f66643a616374697665202e4f6d54497a667b626f726465722d636f6c6f723a23343238356634207472616e73706172656e747d3c2f7374796c653e3c64697620636c6173733d2254574d4f5563223e416e792074696d653c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c7374796c653e2e6f7a61744d7b666f6e742d73697a653a313270783b746578742d7472616e73666f726d3a7570706572636173657d2e6f7a61744d202e794e46736c2c2e6f7a61744d206c697b6c6973742d7374796c652d747970653a6e6f6e653b6c6973742d7374796c652d706f736974696f6e3a6f7574736964653b6c6973742d7374796c652d696d6167653a6e6f6e657d2e794e46736c2e536b556a34632c2e794e46736c20617b636f6c6f723a7267626128302c302c302c302e3534293b746578742d6465636f726174696f6e3a6e6f6e653b70616464696e673a36707820343470782036707820313470783b6c696e652d6865696768743a313770783b646973706c61793a626c6f636b7d2e536b556a34637b6261636b67726f756e642d696d6167653a75726c282f2f73736c2e677374617469632e636f6d2f75692f76312f6d656e752f636865636b6d61726b322e706e67293b6261636b67726f756e642d706f736974696f6e3a72696768742063656e7465723b6261636b67726f756e642d7265706561743a6e6f2d7265706561747d2e536b556a34633a6163746976657b6261636b67726f756e642d636f6c6f723a236635663566357d3c2f7374796c653e3c6c6920636c6173733d22794e46736c20536b556a3463223e416e792074696d653c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494477223e5061737420686f75723c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494541223e5061737420323420686f7572733c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7726616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494551223e50617374207765656b3c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6d26616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494567223e50617374206d6f6e74683c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7926616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494577223e5061737420796561723c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c64697620636c6173733d2254574d4f5563223e416c6c20726573756c74733c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c6c6920636c6173733d22794e46736c20536b556a3463223e416c6c20726573756c74733c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d6c693a3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494651223e566572626174696d3c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d646f63756d656e742e676574456c656d656e7442794964282273742d746f67676c6522292c623d646f63756d656e742e676574456c656d656e7442794964282273742d6361726422293b612626622626612e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2863297b622e7374796c652e646973706c61793d622e7374796c652e646973706c61793f22223a226e6f6e65223b632e70726576656e7444656661756c7428297d2c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c2f6469763e3c7374796c653e2e5a494e6262637b6261636b67726f756e642d636f6c6f723a236666663b6d617267696e2d626f74746f6d3a313070783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b626f726465722d7261646975733a3870787d2e75555047697b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b7d2e4f39673563633e2a3a66697273742d6368696c647b626f726465722d746f702d6c6566742d7261646975733a3870783b626f726465722d746f702d72696768742d7261646975733a3870787d2e4f39673563633e2a3a6c6173742d6368696c647b626f726465722d626f74746f6d2d6c6566742d7261646975733a3870783b626f726465722d626f74746f6d2d72696768742d7261646975733a3870787d2e726c37696c627b646973706c61793a626c6f636b3b636c6561723a626f74687d2e6c634a4631647b6d617267696e2d6c6566743a313670783b666c6f61743a72696768743b7d2e6b437259547b70616464696e673a31327078203136707820313270787d612e6664597371667b636f6c6f723a233442313141387d2e424e656177657b77686974652d73706163653a7072652d6c696e653b776f72642d777261703a627265616b2d776f72647d2e76766a774a627b636f6c6f723a233139363744323b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070787d2e76766a774a6220613a766973697465647b636f6c6f723a233442313141387d2e76766a774a622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e76766a774a622e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69747b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e55506d69742e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69742e415037576e647b636f6c6f723a7267626128302c3130322c33332c31297d2e7835346774667b6865696768743a3170783b6261636b67726f756e642d636f6c6f723a236466653165353b6d617267696e3a3020313670787d2e4170354f53647b70616464696e672d626f74746f6d3a313270787d2e7333763972647b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e7333763972642e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7333763972642e415037576e647b636f6c6f723a233230323132347d2e6d53783145657b70616464696e672d6c6566743a343870783b6d617267696e3a307d2e7639693631657b70616464696e672d626f74746f6d3a3870787d2e584c6c6f58657b636f6c6f723a233139363744323b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e584c6c6f586520613a766973697465647b636f6c6f723a233442313141387d2e584c6c6f58652e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e584c6c6f58652e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e5a54763942627b646973706c61793a626c6f636b7d2e6465497643627b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070783b666f6e742d7765696768743a3430307d2e6465497643622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e6465497643622e415037576e647b636f6c6f723a233230323132347d2e4643557030637b666f6e742d7765696768743a626f6c647d2e58374e5456657b646973706c61793a7461626c653b77696474683a313030253b70616464696e672d72696768743a313670783b626f782d73697a696e673a626f726465722d626f787d2e74486d6651657b646973706c61793a7461626c652d63656c6c3b70616464696e673a313270782030203132707820313670787d2e554874726b7b77696474683a373270787d2e4842544d36647b77696474683a333070787d2e5853377947647b646973706c61793a7461626c652d63656c6c3b746578742d616c69676e3a63656e7465723b766572746963616c2d616c69676e3a6d6964646c653b70616464696e673a3132707820302031327078203870787d2e616d335142667b646973706c61793a7461626c653b766572746963616c2d616c69676e3a746f707d2e5862355652657b636f6c6f723a233139363744327d613a76697369746564202e5862355652657b636f6c6f723a233442313141387d2e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c31297d613a76697369746564202e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438447b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e74416438442e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438442e415037576e647b636f6c6f723a233730373537417d2e614a79694f637b636f6c6f723a233030363632317d2e6f754a3943627b666c6f61743a6c6566743b70616464696e672d72696768743a313670787d2e526f434c6e657b636c6561723a626f74687d2e45594f736c647b646973706c61793a696e6c696e652d626c6f636b3b706f736974696f6e3a72656c61746976657d2e424669395a627b6f766572666c6f773a68696464656e3b706f736974696f6e3a72656c61746976657d2e53374a647a657b616c69676e2d6974656d733a63656e7465723b646973706c61793a666c65783b666c65782d646972656374696f6e3a636f6c756d6e3b6a7573746966792d636f6e74656e743a73706163652d61726f756e647d2e58646c7230647b6f766572666c6f772d783a6175746f3b2d7765626b69742d6f766572666c6f772d7363726f6c6c696e673a746f7563683b6d617267696e3a30202d3870783b70616464696e673a313670782030203136707820323470783b70616464696e672d746f703a3270783b6d617267696e2d746f703a2d3270783b7472616e73666f726d3a7472616e736c617465336428302c302c30297d2e6964673862657b646973706c61793a7461626c653b626f726465722d636f6c6c617073653a73657061726174653b626f726465722d73706163696e673a38707820303b6d617267696e3a30202d3870783b70616464696e672d72696768743a313670787d2e425647304e627b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a746f703b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d7261646975733a3870783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b6f766572666c6f773a68696464656e7d2e55796b5439647b646973706c61793a626c6f636b3b666c6f61743a72696768743b70616464696e672d6c6566743a313670787d2e6e59543751627b636c6561723a626f74687d2e736b566770627b646973706c61793a7461626c653b7461626c652d6c61796f75743a66697865643b77696474683a313030257d2e5647484d58647b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c653b6865696768743a353270783b746578742d616c69676e3a63656e7465727d2e4c70614472627b6d617267696e3a30206175746f203870783b646973706c61793a626c6f636b7d2e766253684f657b70616464696e672d746f703a307d2e4156736570667b70616464696e672d626f74746f6d3a3870787d2e4156736570662e753278314f647b70616464696e672d626f74746f6d3a307d2e787063202e6877632c2e787078202e6877787b646973706c61793a6e6f6e657d2e5275386964627b6d617267696e2d746f703a2d313670787d2e70756e657a7b666f6e742d7765696768743a3730303b6c65747465722d73706163696e673a302e373570783b746578742d7472616e73666f726d3a7570706572636173657d2e7779727758637b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e7779727758632e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7779727758632e415037576e647b636f6c6f723a233230323132347d2e6d4868796c667b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c657d2e575a35474a667b616c69676e2d6974656d733a63656e7465723b70616464696e673a3020323070783b6d696e2d77696474683a31313270787d2e714e394b65642c2e44586b354d657b6d617267696e3a30206175746f7d2e44586b354d657b6d617267696e2d626f74746f6d3a313270787d2e51693946647b6261636b67726f756e643a236666663b626f726465723a303b626f726465722d7261646975733a39393970783b646973706c61793a626c6f636b3b6865696768743a353670783b6a7573746966792d636f6e74656e743a63656e7465723b77696474683a353670783b7a2d696e6465783a307d2e51693946647b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238292c696e7365742030203020302030207267626128302c302c302c302e3130292c696e73657420302030203020302072676261283235352c3235352c3235352c302e3530297d2e51693946643a666f6375737b6f75746c696e653a6e6f6e657d2e5169394664202e685748754a7b646973706c61793a626c6f636b3b6d617267696e3a30206175746f7d2e6a69356a70667b746578742d616c69676e3a63656e7465727d68727b626f726465723a303b626f726465722d626f74746f6d3a31707820736f6c696420236466653165353b6d617267696e3a303b7d2e425579624b652c2e48736e4642667b6d617267696e2d6c6566743a313670787d2e425579624b652c2e6f4d3247417b6d617267696e2d72696768743a313670787d2e584f377268637b6d617267696e3a30202d313670787d2e6949576d34627b626f782d73697a696e673a626f726465722d626f783b6d696e2d6865696768743a343870787d2e664c745873637b70616464696e673a313470783b746578742d616c69676e3a63656e7465727d2e4c796d38577b77696474683a313470783b6865696768743a323070783b706f736974696f6e3a72656c61746976653b6d617267696e3a30206175746f7d2e4164665872627b6d617267696e2d6c6566743a2d313470783b766572746963616c2d616c69676e3a6d6964646c653b646973706c61793a696e6c696e652d626c6f636b7d2e4c796d3857206469767b706f736974696f6e3a6162736f6c7574653b626f726465722d6c6566743a37707820736f6c6964207472616e73706172656e743b626f726465722d72696768743a37707820736f6c6964207472616e73706172656e743b77696474683a303b6865696768743a303b6c6566743a307d2e4979596145647b746f703a3770783b626f726465722d746f703a37707820736f6c696420233735373537357d2e4543554851657b746f703a3470783b626f726465722d746f703a37707820736f6c696420236666667d2e4165515175627b626f74746f6d3a3770783b626f726465722d626f74746f6d3a37707820736f6c696420233735373537357d2e5943553765627b626f74746f6d3a3470783b626f726465722d626f74746f6d3a37707820736f6c696420236666667d2e4963783643647b6d617267696e3a30206175746f203870787d2e6d41646a51637b746578742d616c69676e3a72696768747d2e75456563337b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e75456563332e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e75456563332e415037576e647b636f6c6f723a233730373537417d2e724c736879662c2e426d503574667b70616464696e672d746f703a313270783b70616464696e672d626f74746f6d3a313270787d2e773143334c652c2e426d503574662c2e47354e6242647b70616464696e672d6c6566743a313670783b70616464696e672d72696768743a313670783b7d2e47354e6242647b70616464696e672d626f74746f6d3a313270787d2e6e4d796d65667b646973706c61793a666c65787d2e473565466c667b666c65783a313b646973706c61793a626c6f636b7d2e6e4d796d6566207370616e7b746578742d616c69676e3a63656e7465727d3c2f7374796c653e3c6469763e3c212d2d53575f435f582d2d3e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4141656751494278414226616d703b7573673d414f76566177327466496430584346385649396a682d324f35555069223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f733c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e6178696f732e636f6d3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d224170354f5364223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536d6172742c20656666696369656e74206e65777320776f72746879206f6620796f75722074696d652c20617474656e74696f6e2c20616e642074727573742e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f706f6c697469637326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741586f4543416351417726616d703b7573673d414f765661773139685044464b57637a653030627974527851476576223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e506f6c69746963733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f617574686f72732f6e6577736465736b26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177416e6f4543416351425126616d703b7573673d414f7656617730315a6b4f30626d50386e4370657152386257474959223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e53746f72696573206279204178696f733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6178696f732d64617368626f6172642d313535303631393334332d64653734306436612d656163312d343539392d383262642d3434303533343537376639322e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741336f4543416351427726616d703b7573673d414f76566177305658706145566f434f444153557657784f61466652223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4178696f732044617368626f6172643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c65747465727326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742486f4543416351435126616d703b7573673d414f76566177316255574b54564d7a4944436d736a34536d614c4755223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4e6577736c6574746572733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f776f726c6426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742586f4543416351437726616d703b7573673d414f765661773246776f732d5f6e416d39666e4963486a754b325a53223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e576f726c643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f627573696e65737326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177426e6f4543416351445126616d703b7573673d414f76566177324366426a4a724a72716e5052623241754d614e4953223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e427573696e6573733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e546f702073746f726965733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c6574746572732f6178696f732d616d2d61653065626362352d616432352d346531632d623662642d3138633530393464656330312e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4164364241674145414926616d703b7573673d414f765661773148445a7242345437756b327137384256725676426f223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e4178696f7320414d202d2041756775737420392c20323031393c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f636f756e74726965732d6d6f73742d7269736b2d77617465722d6372697369732d66343066343866392d623032652d346536622d393965642d3136346335636239353333352e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4168364241674145415526616d703b7573673d414f7656617733505243566f5a5532675a494b44336b62615072544e223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e54686520636f756e7472696573206d6f7374206174207269736b206f662061207761746572206372697369733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545617574686f7226616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673513646347743586f4543415951416726616d703b7573673d414f7656617733626475774e6f5934356137397a30464775566e566d223e3c64697620636c6173733d226b43725954223e3c64697620636c6173733d226f754a3943622053374a647a6522207374796c653d2277696474683a343070783b6865696768743a34307078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a343070783b6d61782d6865696768743a34307078222069643d2264696d675f312220646174612d64656665727265643d2231223e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320262331303030333b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e54776974746572202623383235303b206178696f733c2f6469763e3c2f6469763e3c64697620636c6173733d22526f434c6e65223e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838373636383733393733393634382533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177436e6f4543415951424126616d703b7573673d414f7656617733765932387a4a6374626a64305a365357505a344c63223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5477697474657220686173207265696e737461746564204d69746368204d63436f6e6e656c6c27732072652d656c656374696f6e2063616d706169676e206163636f756e74206166746572207468652070726f66696c65207761732073757370656e646564206561726c6965722074686973207765656b2e207777772e6178696f732e636f6d2f6d697463682d6d63636f6e2623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e313920686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838333433333933313336363430322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517743336f4543415951426726616d703b7573673d414f76566177336c4b50674566354d5351724b75784655584d614f41223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e657720746563686e6f6c6f677920697320726573686170696e67206173736574206d616e6167656d656e7420666f722061206e65772067656e65726174696f6e206f6620776f726b6572732c20616e64206d6f7374206f662074686520696e64757374727920686173206e6f74206b65707420706163652e207777772e6178696f732e636f6d2f61737365742d6d616e61672623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837373735323139323939353333332533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744486f4543415951434126616d703b7573673d414f7656617732554b4158715044627577556a736d37626342675874223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5374657068656e20526f73732022667265616b6564206f75742220617420746865206261636b6c61736820746f20686973205472756d702066756e64726169736572206173205472756d70206173736f636961746573207065727375616465642068696d20746f20676f206168656164207769746820746865206576656e742061742068697320536f757468616d70746f6e206d616e73696f6e2e207777772e6178696f732e636f6d2f7374657068656e2d726f732623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837323436373432383530373634392533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744586f4543415951436726616d703b7573673d414f7656617733683854556374412d32397252656e584742497a7a5f223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4265796f6e6420726570616972696e6720616e6420696d70726f76696e6720726f61647320616e64207369646577616c6b732c20636974696573206861766520616e206f70706f7274756e69747920746f206275696c6420696e667261737472756374757265207468617420636f756c64206f70656e207570206e6577206d6f62696c697479206f7074696f6e7320616e6420696e637265617365206163636573736962696c6974792c207772697465732045787065727420566f6963657320636f6e7472696275746f722048656e727920436c6179706f6f6c2e207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393836373138323634333430343830322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177446e6f4543415951444126616d703b7573673d414f765661773056326641356f4573505f6b723267467962676b5568223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e414d442043454f204c697361205375206469736375737365732077697468204178696f732068657220636f6d70616e79277320726573757267656e636520616e6420626174746c6573207769746820496e74656c207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323120686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f6769746875622e636f6d2f6178696f732f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4150656751494242414226616d703b7573673d414f7656617733486a474b6775516d6342745456445566344d665072223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e6178696f732f6178696f733a2050726f6d697365206261736564204854545020636c69656e7420666f72207468652062726f7773657220616e64202e2e2e202d204769744875623c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f6769746875622e636f6d202623383235303b206178696f73202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e636f6e7374206178696f73203d207265717569726528276178696f7327293b202f2f204d616b652061207265717565737420666f72206120757365722077697468206120676976656e204944206178696f732e2067657428272f757365723f49443d31323334352729202e7468656e2866756e6374696f6e2028726573706f6e736529207b202f2f2068616e646c652073756363657373a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517757347745486f4543416b51416726616d703b7573673d414f7656617733565033564769536e46355355474c45653730387037223e3c696d6720636c6173733d2255796b5439642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a373270783b6d61782d6865696768743a37327078222069643d2264696d675f32332220646174612d64656665727265643d2231223e3c2f613e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e4178696f733c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e576562736974653c2f6469763e3c2f7370616e3e3c64697620636c6173733d226e5954375162223e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e204974206f6666696369616c6c79206c61756e6368656420696e20323031372e2054686520736974652773206e616d65206973206261736564206f6e2074686520477265656b3a202623373934303b26233935383b26233935333b26233935393b26233936323b2c206d65616e696e672022776f72746879222e203c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516d684d77456e6f4543416b51427726616d703b7573673d414f76566177306a76706f32364a65425a684a7638496157384e7163223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e57696b6970656469613c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22766253684f65206b43725954223e3c64697620636c6173733d22415673657066223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4f776e65723c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e4178696f73204d6564696120496e633c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d2241567365706620753278314f64223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4c61756e636865643c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e323031373c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e50656f706c6520616c736f2073656172636820666f723c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22787063223e3c64697620636c6173733d22783534677466223e3c2f6469763e3c6469763e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c206e65777320736f7572636573206c6973743c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d434e4e26616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335343394f4c7a4b537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d7851685866497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a333844437549695632646e504477435833376e476b414141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454149223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e434e4e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d506f6c697469636f26616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d444b504e7971334e4a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45556b6e6c4a4f52573153516e6752566c4778516e414d566a795f4d4d7339495f38556f3659504c5f415957786b5773484148354f5a6b6c6d636e3541484749687547584141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945414d223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e506f6c697469636f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d48756666506f737426616d703b737469636b3d483473494141414141414141414f4f5155654c537a395533794d67744b6b68504d704c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45617a5479447a65714e7753796f45594131575562464363417857504c3877797a306a5f78536a7067387638426862475261776348715670615148357853554175376a463070634141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454151223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e48756666506f73743c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4e505226616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335344596f7a6a47537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d785168576e463663586e534b6b5176454e444b504e79713368484979636f734b30704f67696b416d5173586a4337504d4d394a5f4d55723634444b5f675956784553757a583041514142514b584c32514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454155223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e6174696f6e616c205075626c69632052612e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b6e6577732b736f75726365732b6c69737426616d703b737469636b3d483473494141414141414141414f4f514d5a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794b476671322d515870786564497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a3338444375496756747a514146695a305070774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454159223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f31312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22687763223e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c20706f6c69746963616c2077656273697465733c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d536c6174652b4d6167617a696e6526616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d4449724e7259776b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a5762787875565730493547626c4642656c4a70786752426b456c7a4d334b636f70796f524c47795a55705656433253564a4b5354715562576c55626c7a796931484b423666564453794d69316a35676e4d5353314956664250544536737938314942452d4b766d72674141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454167223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536c617465204d6167617a696e653c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d42757a7a4665656426616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d44637279796e4b4e5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a69303878677455626d6363626c5674434f526d3552515870536163594f6345795a735847466c414a694b6c514365506b797051714b4e736b4b61556b486371324e436f334c766e464b4f5744302d6f4746735a467242784f7056565662716d704b5144564e44327173774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416b223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e42757a7a466565643c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4461696c792b4b6f7326616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d453675544b6b796b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524a67473642736b3653556b6e516f32394b6f334c6a6b46364f554430367247316759463746797569526d356c5171654f635841774447505a396673774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416f223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4461696c79204b6f733c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d54616c6b696e672b506f696e74732b4d656d6f26616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d456c4b4b556b336b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524c47795a5570565641323244596f32394b6f334c6a6b46364f5544303672473167594637454b6879546d5a47666d705373453547666d6c5251722d4b626d35674d415637446f6d62304141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454173223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e54616c6b696e6720506f696e7473204d652e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b706f6c69746963616c2b776562736974657326616d703b737469636b3d483473494141414141414141414f4f514d5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a6930387863756e6e366873596d6363626c5674434f526d3552515870536163594f6345795a735847466c414a63374f796e4b4a6371495278636d564b465a52746b7052536b67356c57787156473566385970547977576c314177766a496c5938386744456f63564e75514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454177223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f32312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c68723e3c64697620636c6173733d226475662d68223e3c64697620636c6173733d22664c74587363206949576d346222206f6e636c69636b3d227870287468697329223e3c64697620636c6173733d224c796d3857223e3c64697620636c6173733d2241655151756220687763223e3c2f6469763e3c64697620636c6173733d2259435537656220687763223e3c2f6469763e3c64697620636c6173733d2249795961456420687778223e3c2f6469763e3c64697620636c6173733d2245435548516520687778223e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4159656751494252414226616d703b7573673d414f76566177333867573437455f39394a326a683746384149573439223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320287765627369746529202d2057696b6970656469613c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f656e2e77696b6970656469612e6f7267202623383235303b2077696b69202623383235303b204178696f735f2877656273697465293c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320287374796c697a6564206173204158494f532920697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e66616365626f6f6b2e636f6d2f6178696f736e6577732f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a415a656751494178414226616d703b7573673d414f76566177306e6d4a4834444273432d317679714158357832346a223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d20486f6d65207c2046616365626f6f6b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e66616365626f6f6b2e636f6d202623383235303b202e2e2e202623383235303b204272616e64202623383235303b2057656273697465202623383235303b204e6577732026616d703b204d6564696120576562736974653c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5468657265206973206e6f207761792052657075626c6963616e732063616e206368616e6765206269727468207261746573206f7220637572622074686973207472656e64202623383231323b20616e642074686572652773206e6f7420612073696e676c652064656d6f67726170686963206d6567617472656e642074686174206661766f72732052657075626c6963616e732e206178696f732e636f6d2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e68626f2e636f6d2f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4161656751494168414226616d703b7573673d414f7656617730314959735a763273624653415f745778396f6a6850223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d204f6666696369616c205765627369746520666f72207468652048424f20536572696573202d2048424f2e636f6d3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e68626f2e636f6d202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4b6e6f776e20666f722064656c69766572696e67206e6577732c20636f7665726167652c20616e6420696e7369676874207769746820612064697374696e6374697665206272616e64206f6620736d61727420627265766974792c204178696f73206f6e2048424f2068656c707320766965776572732062657474657220756e6465727374616e642074686520626967207472656e6473a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e52656c617465642073656172636865733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a7326616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454145223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206a733c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a73223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454149223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732068626f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e6726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a364241674245414d223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206d65616e696e673c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e67223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76756526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454151223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207675653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b767565223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b666574636826616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454155223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732076732066657463683c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b6665746368223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e617469766526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454159223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207265616374206e61746976653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e6174697665223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f7726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454163223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732074762073686f773c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f77223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454167223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732063646e3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c666f6f7465723e203c6469763e20203c64697620636c6173733d225a494e62626320787064204f396735636320755550476920426d50357466223e3c64697620636c6173733d226e4d796d6566204d5578476264206c794c776c63223e3c6120636c6173733d226e424445316220473565466c662220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b65693d704d524f586250624472505339414f426d4b5059425126616d703b73746172743d313026616d703b73613d4e2220617269612d6c6162656c3d224e6578742070616765223e4e657874202667743b3c2f613e3c2f6469763e3c2f6469763e203c2f6469763e2020203c6469762069643d226d436c6a6f62223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d2f536572766963654c6f67696e253346636f6e74696e7565253344687474703a2f2f7777772e676f6f676c652e636f6d2f73656172636825323533467125323533446178696f7325323532366f7125323533446178696f73253235323661717325323533446368726f6d652e302e36396935396c326a306c336a36396936302e3831316a306a372532353236736f75726365696425323533446368726f6d652532353236696525323533445554462d38253236686c253344656e26616d703b73613d5526616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517873384343475126616d703b7573673d414f765661773338654a695f306b32366743644b726d315057417979223e5369676e20696e3c2f613e3c2f6469763e3c6469763e3c6120636c6173733d226b73545534632220687265663d22687474703a2f2f7777772e676f6f676c652e636f6d2f707265666572656e6365733f686c3d656e2d434126616d703b66673d3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735135665543434755223e53657474696e67733c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f707269766163792f3f66673d31223e507269766163793c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f7465726d732f3f66673d31223e5465726d733c2f613e3c2f6469763e3c2f6469763e20203c2f666f6f7465723e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220686c3d27656e2d4341273b2866756e6374696f6e28297b76617220623d746869737c7c73656c662c643d2f5e5b5c772b2f5f2d5d2b5b3d5d7b302c327d242f2c653d6e756c6c3b76617220663d646f63756d656e742e717565727953656c6563746f7228222e6c22292c673d646f63756d656e742e717565727953656c6563746f72282223736622292c6b3d672e717565727953656c6563746f7228222e73626322292c6c3d672e717565727953656c6563746f7228225b747970653d746578745d22292c6d3d672e717565727953656c6563746f7228225b747970653d7375626d69745d22292c6e3d672e717565727953656c6563746f7228222e736322292c703d672e717565727953656c6563746f7228222e7822292c713d6c2e76616c75652c723d5b5d2c743d2d312c753d712c772c782c793b717c7c2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a28213129293b66756e6374696f6e207a2861297b6966286b2e636c6173734c6973742e636f6e7461696e732822657362632229297b76617220633d6b2e636c6173734c6973742e636f6e7461696e732822636873626322292c683d6b2e636c6173734c6973742e636f6e7461696e73282272746c73626322293b612626286e2e7374796c652e646973706c61793d22626c6f636b222c633f28672e7374796c652e626f726465725261646975733d2232307078203230707820302030222c6e2e7374796c652e626f72646572426f74746f6d3d2231707820736f6c69642023444645314535222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302030223a223020323070782030203022293a6b2e7374796c652e626f726465725261646975733d683f22302038707820302030223a2238707820302030203022293b617c7c286e2e7374796c652e646973706c61793d226e6f6e65222c633f28672e7374796c652e626f726465725261646975733d2232307078222c6e2e7374796c652e626f72646572426f74746f6d3d226e6f6e65222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302032307078223a223020323070782032307078203022293a6b2e7374796c652e626f726465725261646975733d683f223020387078203870782030223a22387078203020302038707822297d7d66756e6374696f6e204128297b672e717565727953656c6563746f7228225b6e616d653d6f715d22292e76616c75653d753b672e717565727953656c6563746f7228225b6e616d653d6171735d22292e76616c75653d22686569726c6f6f6d2d7372702e222b28303c3d743f743a2222292b222e222b28303c722e6c656e6774683f22306c222b722e6c656e6774683a2222297d0a66756e6374696f6e204328297b773d6e756c6c3b69662878297b76617220613d222f636f6d706c6574652f7365617263683f636c69656e743d686569726c6f6f6d2d73727026686c3d222b686c2b22266a736f6e3d742663616c6c6261636b3d685326713d222b656e636f6465555249436f6d706f6e656e742878293b22756e646566696e656422213d3d747970656f6620647326266473262628612b3d222664733d222b6473293b76617220633d646f63756d656e742e637265617465456c656d656e74282273637269707422293b632e7372633d613b6966286e756c6c3d3d3d6529613a7b613d622e646f63756d656e743b69662828613d612e717565727953656c6563746f722626612e717565727953656c6563746f7228227363726970745b6e6f6e63655d222929262628613d612e6e6f6e63657c7c612e67657441747472696275746528226e6f6e63652229292626642e74657374286129297b653d613b627265616b20617d653d22227d28613d65292626632e73657441747472696275746528226e6f6e6365222c61293b646f63756d656e742e626f64792e617070656e644368696c642863293b783d6e756c6c3b773d73657454696d656f757428432c353030297d7d0a66756e6374696f6e204428297b666f72283b6e2e66697273744368696c643b296e2e72656d6f76654368696c64286e2e66697273744368696c64293b723d5b5d3b743d2d313b7a282131297d66756e6374696f6e204528297b76617220613d6e2e717565727953656c6563746f7228222e73637322293b61262628612e636c6173734e616d653d2222293b303c3d743f28613d6e2e6368696c644e6f6465735b745d2c612e636c6173734e616d653d22736373222c713d612e74657874436f6e74656e74293a713d753b6c2e76616c75653d717d6c2e6164644576656e744c697374656e65722822666f637573222c66756e6374696f6e28297b66262628662e7374796c652e646973706c61793d226e6f6e6522297d2c2131293b6c2e6164644576656e744c697374656e65722822626c7572222c66756e6374696f6e28297b4428293b66262628662e7374796c652e646973706c61793d2222297d2c2131293b6c2e6164644576656e744c697374656e657228226b65797570222c66756e6374696f6e2861297b713d6c2e76616c75653b793d21313b31333d3d612e77686963683f4128293a32373d3d612e77686963683f284428292c66262628662e7374796c652e646973706c61793d2222292c713d752c6c2e76616c75653d71293a34303d3d612e77686963683f28742b2b2c743e3d722e6c656e677468262628743d2d31292c452829293a33383d3d612e77686963683f28742d2d2c2d313e74262628743d722e6c656e6774682d31292c452829293a28613d71293f2870262628702e7374796c652e646973706c61793d2222292c783d612c777c7c4328292c753d61293a2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a282131292c4428292c753d22222c793d2130297d2c2131293b6d2e6164644576656e744c697374656e65722822636c69636b222c412c2131293b702e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e76616c75653d22223b702e7374796c652e646973706c61793d226e6f6e65223b7a282131297d2c2131293b6b2e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e666f63757328297d2c2131293b77696e646f772e68533d66756e6374696f6e2861297b6966282179297b4428293b303d3d615b315d2e6c656e67746826267a282131293b666f722876617220633d303b633c615b315d2e6c656e6774683b632b2b297b76617220683d615b315d5b635d5b305d2c763d646f63756d656e742e637265617465456c656d656e74282264697622293b762e696e6e657248544d4c3d683b762e6164644576656e744c697374656e657228226d6f757365646f776e222c66756e6374696f6e2842297b422e70726576656e7444656661756c7428293b72657475726e21317d2c2131293b683d682e7265706c616365282f3c5c2f3f623e2f672c2222293b762e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2842297b72657475726e2066756e6374696f6e28297b743d423b4128293b4528293b4428293b672e7375626d697428297d7d2863292c2131293b6e2e617070656e644368696c642876293b7a282130293b722e707573682868297d7d7d3b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b66756e6374696f6e20622861297b666f7228613d612e7461726765747c7c612e737263456c656d656e743b612626224122213d612e6e6f64654e616d653b29613d612e706172656e74456c656d656e743b61262628612e687265667c7c2222292e6d61746368282f5c2f7365617263682e2a5b3f265d74626d3d697363682f29262628612e687265662b3d22266269773d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e7457696474682c612e687265662b3d22266269683d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e74486569676874297d646f63756d656e742e6164644576656e744c697374656e65722822636c69636b222c622c2131293b646f63756d656e742e6164644576656e744c697374656e65722822746f7563685374617274222c622c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c212d2d206363746c636d2035206363746c636d202d2d3e3c746578746172656120636c6173733d2263736922206e616d653d2263736922207374796c653d22646973706c61793a6e6f6e65223e3c2f74657874617265613e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b76617220736e3d27776562273b2866756e6374696f6e28297b76617220713d66756e6374696f6e2861297b76617220633d77696e646f772c643d646f63756d656e743b69662821617c7c226e6f6e65223d3d612e7374796c652e646973706c61792972657475726e20303b696628642e64656661756c74566965772626642e64656661756c74566965772e676574436f6d70757465645374796c65297b76617220623d642e64656661756c74566965772e676574436f6d70757465645374796c652861293b696628622626282268696464656e223d3d622e7669736962696c6974797c7c22307078223d3d622e686569676874262622307078223d3d622e7769647468292972657475726e20307d69662821612e676574426f756e64696e67436c69656e74526563742972657475726e20313b76617220663d612e676574426f756e64696e67436c69656e745265637428293b613d662e6c6566742b632e70616765584f66667365743b623d662e746f702b632e70616765594f66667365743b766172206b3d662e77696474683b663d662e6865696768743b72657475726e20303e3d662626303e3d6b3f303a303e622b663f323a623e3d28632e696e6e65724865696768747c7c642e646f63756d656e74456c656d656e742e636c69656e74486569676874293f333a303e612b6b7c7c613e3d28632e696e6e657257696474687c7c642e646f63756d656e74456c656d656e742e636c69656e745769647468293f343a317d3b76617220723d652c763d736e3b66756e6374696f6e207728612c632c64297b613d222f67656e5f3230343f617479703d63736926733d222b28767c7c2277656222292b2226743d222b612b2822266c6974653d312665693d222b722b2226636f6e6e3d222b2877696e646f772e6e6176696761746f72262677696e646f772e6e6176696761746f722e636f6e6e656374696f6e3f77696e646f772e6e6176696761746f722e636f6e6e656374696f6e2e747970653a2d31292b63293b633d222672743d223b666f7228766172206220696e206429612b3d632b622b222e222b645b625d2c633d222c223b72657475726e20617d66756e6374696f6e20782861297b613d7b7072743a617d3b77696e646f772e77737274262628612e777372743d77696e646f772e77737274293b72657475726e20617d66756e6374696f6e20792861297b77696e646f772e70696e673f77696e646f772e70696e672861293a286e657720496d616765292e7372633d617d0a2866756e6374696f6e28297b666f722876617220613d2b6e657720446174652d77696e646f772e73746172742c633d782861292c643d302c623d302c663d302c6b3d646f63756d656e742e676574456c656d656e747342795461674e616d652822696d6722292c6d3d2226696d6e3d222b6b2e6c656e6774682b22266269773d222b77696e646f772e696e6e657257696474682b22266269683d222b77696e646f772e696e6e65724865696768742c413d66756e6374696f6e28682c7a297b682e6f6e6c6f61643d66756e6374696f6e28297b623d2b6e657720446174652d77696e646f772e73746172743b7a26262b2b6e3d3d66262628643d622c742829293b682e6f6e6c6f61643d6e756c6c7d7d2c743d66756e6374696f6e28297b6d2b3d2226696d613d222b663b632e6166743d643b7928772822616674222c6d2c6329297d2c6e3d302c423d302c673d766f696420303b673d6b5b422b2b5d3b297b766172206c3d313d3d712867293b6c26262b2b663b76617220753d672e636f6d706c657465262621672e6765744174747269627574652822646174612d646566657272656422292c703d7526264e756d62657228672e6765744174747269627574652822646174612d696d6c2229297c7c303b752626703f286c26262b2b6e2c70262628673d702d77696e646f772e73746172742c6c262628643d4d6174682e6d617828642c6729292c623d4d6174682e6d617828622c672929293a4128672c6c297d647c7c28643d61293b627c7c28623d64293b6e3d3d6626267428293b77696e646f772e6164644576656e744c697374656e657228226c6f6164222c66756e6374696f6e28297b77696e646f772e73657454696d656f75742866756e6374696f6e28297b632e6f6c3d2b6e657720446174652d77696e646f772e73746172743b632e696d6c3d623b76617220683d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e74696d696e673b68262628632e727173743d682e726573706f6e7365456e642d682e7265717565737453746172742c632e727370743d682e726573706f6e7365456e642d682e726573706f6e73655374617274293b7928772822616c6c222c6d2c6329297d2c30297d2c2131297d2928293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e66756e6374696f6e205f736574496d6167657353726328652c63297b66756e6374696f6e20662861297b612e6f6e6572726f723d66756e6374696f6e28297b612e7374796c652e646973706c61793d226e6f6e65227d3b612e7372633d637d666f722876617220673d302c623d766f696420303b623d655b672b2b5d3b297b76617220643d646f63756d656e742e676574456c656d656e74427949642862293b643f662864293a2877696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d2c676f6f676c652e6969723d676f6f676c652e6969727c7c7b7d2c676f6f676c652e6969725b625d3d63297d7d3b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f6a7065673b6261736536342c2f396a2f34414151536b5a4a5267414241514141415141424141442f3277434541416b474277674842676b494277674b43676b4c445259504451774d445273554652415749423069496941644878386b4b4451734a4359784a7838664c5430744d5455334f6a6f364979732f52443834517a51354f6a634243676f4b4451774e47673850476a636c487955334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e2f2f4141424549414367414b414d4249674143455145444551482f7841415a4141414441514542414141414141414141414141414141414267634243414c2f784141734541414241774d434241554541774141414141414141414241674d454141555242694548456a4642453147426b61456959634852464256782f3851414751454141674d424141414141414141414141414141414141514944424155412f385141485245414177454141674d424141414141414141414141414141454341784a4242424d7842662f61414177444151414345514d5241443841754e655843416e4a3644725735706631746450362b794f6870584b2b2b5044622b3265703971664f48644b56325236364c4b4862364632343858744c57366139456b6d64346a4b696c5254487944397763394b66597237556d4d3149595746744f6f43304c4232556b6a4949394b3566313559333151303374686e4d6474774d504c386c455a542b765565645666675271495858536f746a7a67564a7468434d5a33384d377039747836553232667274794a342b767479566c506f7242306f71496e4d5053705a7275354766656a486279576f333044486452362f67565272784a6369323139356c70546a6752394345444a4a376256504e4c574b5a4c76726239776a504962625558564b6451527a4b37664e58764334787931726f797630656438635a58305a4470526d586f682b7879414f61537965645248527737672b687837564265476c346530667841615a6d6b7474716456436d4950626647663841516f44357271554441785541343161496e6a564b62725a59456951334f547a752f7741644255554f4a786b37644d6a487a564f716455365a705a776f68536a6f45644b4b576548647a6e584c53634a64316a5078357a4b664265513867704a4b647562667a4739464b4f4d7441414859555555414734464742525252434742355555555678782f2f32515c7833645c783364273b76617220693d5b2764696d675f31275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414945414141424943414d414141446d6d575965414141416f6c424d5645556949694c2f2f2f38414141415a47526b644852337537753751304e41534568494b6d654156465256675947414f4467352b666e3757317462382f507a643364314c533073784d5447506a342b656e703675727134714b69705a57566d30744c544477384e766232382f507a2f303950546a342b4f38764c783364336547686f596a484255556572416545414137676138416d657133312f496a46674d586135704570654d416b64375336506f61585952666b376f4f6a63346453474d476e2b6f564141416a445141664f6b77684c6a636b4141416256485a4d6b39507941414143636b6c455156526f6765325732334c694d41794748646b4a635135415349434568435464412b774a324c624c2b372f61536e4b67334f4f5a6e646e526431484c78575039746e384a6c42494551524145515241455152442b41347a57326a794d48476a3661397a6e4d3044306662314e30356e784b7142496b6d53706c55356f744e4d2f6d7655475a355149564c614e366e49444c6d74716d374c4d647542527761665051524345566b474634787a504452454743326944494d633855474a414c48597036656e647450497034637458334447624b656877334550615577616c777943495145456533476c5342634e747376636d3458434d762b47474853697a6f353033674b6d4441517772674a7254686537675331764d6359676958756a4c43324d63662f394247326f464b787a7a4d7544444f77554e35616f4c6f2f6f35793654374b5145324e4b536542507938785045766c395135674e78677a4b5341336d434c486a5441463751636e414b6f4275767246635a54484d66734144532b556650626730384b5345374231773137537235685439617235554e3150696e6766496b764c37387a50697157576e4d33475374594f3463517475645046704d5238384b54684665386773766261454e33566e594350346854554842644f67556b726f5a37626379564879652b6f51744f72316a30624331584456514a44362f51756c54634c6b704949616c797168612b732b6435666345724f492f4b724e45414c62416670764d354252326e785a57574c5a4c73686d67414d4d6e48367a7a4a4f39727739416344324f4b6550643345766e2b73526d35417051565930726c4436685768776c7277706542775242396552347a342f6b4f36434e634673395170534e6c355965344d6d5042547446585a6565764c65415778693236746f48646d6d4538393052547452316465676448686255493934326d7747313075787750486d6973647a613563515554544e354f355351766168507870706e4c73646a3671635478667239663361574c364c4d75474e5956416b634a3551316c67557932365254335947532f4457716a7262652b704934334966574a6e6941733577726e4c6f6748774e387273746b3754377858724a623867434949674349496743494c77442f6b4c6b327368546d377038716741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3233275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645584d4141442f2f2f2f4b4141444c4141442b2b2f763435655879304e44514e7a666f72713754516b4c72744c54696b5a484d4a53584b44772f34346548484141446f7036663939766235367572383866483031396676784d54676834664f49694c61626d37636433666b6d4a6a78793876575631666e6f4b445453456a55546b3759596d4c4e4768726666332f514d444475766231676d3047614141414758306c455156526f6762576234614b714b68434662636a4b4c44484e637165563758722f5a37774774484d424b70347238394f5154324341575441464732584868635757595855706e337661373571345341363249734e325349713432584861503875344374385642467a5a31564a36382f50675243774941736149732f4b6154634e6c61636d346546395538506a5a484261424d6b714e316d31755442622b4b30507265446d68645a63316f2b373762585852706864346a446a51354376385562676969783033336d387236414e7548695a4f764c452f4f65475746374a5830414d383952656e327346356c67333176573844486b365733756751387a46653367785659414b76773539487a566a37546d507451324131396e6e384d6779386a725950674e6c75654c686265367947654b45447277754d4866716a48484b636376534441626a614f78546e6d333765316f585841526f443369354b6e477654684c4665337145304b6e67766e58385039394c3442376a55696a4d57586174566358706f79317a762f452f3043756965726c61766e346436486b704c506e4e7269794e4f74304a3953486f4734726e50627a51586f4470524c592f6c34715758787736682b727442464d38756b637165525256396e48362b377255395734444c573763384f3364724c634364714c44796374776736753576477a4b4279647053616162325876416e74724d3273514967443858444d4a466c617a4c47734470333637794c702b6b39716f2f79363646544c58753261735633524d537a2b4261565969535037592f616a722f64473156754f574e734a346862614f4c5446705a63756b5834567644614375695734612b6665516856306e74364c392f393041372b7531507942707234612b6c5563464b7133674d6933494c452b68762b6a64676645476f5577456738496a454e30472b3470596b4958416d497245414f6a304d4c4a5a44747865386c64756f556f4278505a3241673136495170366b5265765544412f3661426d5272345767704e44454b3359464d5241727577486235464835547779676253326f2f55506252424744417a4b6b52554f494f4446672b445569524b464a446b667345494a585467476f65342f354457336467384b696d41646b394d2b70557a39794172446c4d416759557638746b734b507732423359397047786c6734436c593973594a506d6f5475513751496c44394f6a47314175463276636974324241656e37345168512b6367524e3732584f394459674d654163726e4157423745787478413556304a646d7271455269516d41596f48336a75452f676a436d4b3038657352474f78465a495842307237774347534e384a73664b4e6b632f41454445744e673159337631444c72436368494e43666d38477a704436693233514d63644644734561695544473746753556486f4654417141537057666f444b744652595a5261654151714266794c3673596a554a3263354f673346342f4134476d4a55742f507641476c6b74485554656b524b4a58526f734374654f5552794e6269425467325a48655051435658387a31757852642f514b566b674d42714f506561462f694a55753851382b424a78377a41674552732b656f2f444a77644b4b505565793978426941396f454b706276712f3650384464316563427161366d526e347a4c4842596d7145634877316377767871456b706d5733506f664d73774d5664587a34586d72715a47336a456f4e73537063344d314134564c65706d6271424e795352506a304474356b5a47716462626e4c6d416d674b5736755a70385a765a674158347a554e737862596c6454626745734931715752513363774d58427a4252355477683256325a714332664d717a31462b6a69544d434678685a53415673584f504f436453556a4e694b4e336f5435775271536b61637752386172596c7a416e55464c4657785436436d5a4f5235757a5931484941702b4d4967634d6b4d4a625049344a6b445542755a51534436694f304d33674749303273456d454f594c34562f6a74757a425a68324c483571517a414d314f3970684d6948726467473542335438794847674f676a37436165645a74744177375a4b44416a773866674c4856326f4f59335574313074754c3567526e653030683138333168667544696851366852616b65674b69413154334e3766504d4278447661526775715436416d674a4764654d46695063303671723336524734534347456b3165393667782b4b6c43635347493269415834395248426b506c4c612f6f486f4c7a4777767966737757493236343862792f454d3345496b4a783731314c4e784257734a6c335746694157495855477a786e6a49764249554479477653595073554f494a5042693632505961536f314a3637726b336c6b79347a304673304f6d4f4245396e77767a572f6b6c554b75726d74776f6f34416c31712b45612f7335664463464c6f427732506a4f67397871316f54373953547449634b5746374179797069644645654a4c3157706555443239664b335a37304d6b334a554b507550784d74556d58314a776e4c596b5941454e6853495a536875676b5933654c5836334c5455305a356f566335624e594d476d6b587266506679576e4746374d6f6d38537a547772567158754839615231386d6c4150512b696177574e767339752b5351672f78336757525777586b47786d414a5561553239466f356c62516f56366335546556674474724b6b4d5864355a5434467950686754716d777a5a446a55435136794a56483535354644657a566b7a76397275416d46796e48356c467035434c5a652f566d397878474a3756477565484f71577565666e62694669522f76443672367a694d7a6c45364957462b6357783257696f7165386266377833613856746a55586c7847547845786e64362f366d674e5349656c476c334f4437704c5262626271746a6b76334466783765365a7176754978327a3374357262522f54667748444f747262744e7643415541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f33275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416456424d5645584e4742332f2f2f2f4a4141444d414144373850444d44685856556c4c4d4642726e73612f37382f505556316a76794d62504953583032747276797372464141446a6b35585150554c4d42512f787a39444d41416a36362b765351304c2b2b76726e724b7a32354f5471754c6661654866585a4754737672375a6348445961576e6669597664666f48504d54446a6f364854536b765350446e6a6d70765a704856754141414461306c455156526f67653258323361714d42424177345159735642705141533543496a2b2f79636553494a415346307439727a4e6672484e5a546245535449536769414967694149676941496769414973676b6d597141514338614e446a3567616541324c4d4f48475749646e43625876413772706a6f5145504f78784f395a7a423461664b342b5448547a776769304c664a4742743852594c6f314f6a6b4b4c3768387a5a5277433370536d466169692f71474c49597957484d5a65353947547545615a57636433413175766c59366337496948683845396b5044783079343834615747414a6e545342554c7832466774646e5938776c595376685949692f46626f7668653563534c6c6c56456774517363723645736832495673496153352b58707a59526e6d6564366b6d57342b4a2f53566b454a6d695a5574684841596661653036594f48355578344261423938736230324f37564d4f386758676e4a555350484a7570766d43397058476a6635533644437771515a3072495366794d4b4b4251442b2f434b2b4759467141656a7131377756566871696e704f6656444b61526b546e794d354e413966555049495657724449766748496f6d4a72526343416e7a737a484d5a69487235462b6e75316a47376e644b502b70734e677048657a594c395174326c4b7942306c6b3133527731636276514d535a4f634f7173684379525231314e7477726a5175367473583042726464434168394b7446564935596f477134746a6a4c312b696c784f3844634c5a614b6e31685674413475513357574f4a5275467a4a636e5832564c476461644c4549563646787346616f63574f304a4f6162772f6c346f4f696b6b7470775277354833763453326e486b6c394137764c656d5864556b50746955563356386b5457354e6d7353574e46444c6262483561465062596d2f64467235745736674a356561545268326c67572f37456d6c704f646f365479334a357150744d4877366865314c48413456737731554e66444f625347664f4c4f7436544448664962453053665464754644686d6a57526e6c314c5a766952463265795273584d4c2f4c532f78636d5562654d706134792f654c56526e2b654b6645494e4449494e357559525351317054514734466e4f674856785849774c364b456876326969464a35344e7734544c70447049756f422b744c4f646f586370422f716e48756e5432466e2b32587068552f466e4b69612b5567684b4d4d666b777535366b517a737048474b625065746f564d714f5663474950507859534e6c586e575271476a314c566a665a5350306a55446a4b46743138492b2b71764e4f4e2b49335276342b2b3474345345482b755459394c454b3245354658656d4d41586a42356b7058505953656a63435a4a5536376937425365616d6d333355414e4f39435858304f534f7149536d48542f45554476316c4e77714e3371485568766f6a6b2b2f746e614a39432f72484b764375714b7271756d75424c6737634958466e304434564271597838742f6e453571394b735239642b324446776d647651706e61702f5a6a7665335563475a72654241454152424541524245415242454154354466384152776f2b4948597561487741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f35275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414555414141424643414d414141417255397362414141415946424d564555414141442f2f2f2b4b696f724f7a733553556c4c613274716e70366672362b7648783866382f507a7a382f4e61576c712b7672342b506a373239765a7763484376723639396658316b5a47524d544579676f4b41774d44415044772b31746257616d706f6749434156465255714b6971546b354e455245527061576b354f546d4d703246774141414239456c455156525968613257366136434d4243465757565241554655334f3737762b55464539716d6338704d577336764575424c4d3265324b4e354455534c53536631775171386a6d5470467959522f494f574b4d7670446e73554b4b57372b6c4c4f3653756f506958704675516451486f7079436143552b317255424667304e53736c44376a4b50686131697649496f4f6a3862774d6f2b31696b382f2f7044376d70713452594e4f6a6d456b424a464157336f3965677a3333716b67704c2f4957554d74626e613879712b6f4f55334b436b504b57476b4c6b2b39455042553742466333336f454e55383551417072554535386843485258654430764951522f366e4275584151796f4969577144346d39526246427946754c492f3936676a41304c69612b51636a556f47513978744b695451526d366736334b707277677054446a516a5353752b447633473857585777497a7639736d2f4b774b5868456a2b30734e3657304b572f3374323652477666702f792b5351443558365730497a6e3947695530706653696454656c384b4b51366662616f4737486f374548356b43727947644666472b49316f742b37574554794837636f527353696e76306c79596c4963486d4c79505770696f6d6c43495972586c476e2f766a54373447484f4b624932672b587336542f347847394474526663415555624e487170456e636b474f4c576f764e4a47366f674a447a6d672f4c67324334596f745550357a5049386b774b707a2f4b71435262502f424c55706c7139436934365a46432b584f513271592f35504b2b5569306f6d4b4c50757239334745465659547a58776430336c7745467545527259654e7a434b38464f6946456d775956425675555471676f686256514d696b5034674769555766444d6859746951646974632f7555735465446a676a616741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f37275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141417146424d56455541414141795a737a2f4d77372f2f2f2f304e42466246776f70544a497a614e424f546b374c793873725973744f6564497156616f735a644258614a442f4141442f4c77436d57452f4e4b51757071616e2f61463956664e4c4d31753854574d6b2b506a372f7a4d725330744b4868346663334e79337437664377734a3565586d546b355072362b76464b51314b45676767505855794d6a4b656e7035746257332f3239556348427a2b764c622f66572f2f6548442b7070372b366555724b7973524552466a5932502f5755784663744259574668634477426c5555395759486b6636744c75414141427a456c455156526f67653359363036444d42694134627036574f64685536596955416271594e4e4e6e4b4c652f35324a5543627041637570662f7a65784d515136324f686443424345415242454152424550516675314a302f3944686c7a36654b554d6a525964484863446a4532564467516571414151515141414e67444e6a344351506a615a7955674358646c365366657453516f676678633341367a794556334f704b49414f7a725057464f2f7a466d746463484a614445485a31354e736b674a496967472b673674353478596758756e4d6b474235555173776d324e37454339616750685a4f4b6e36494259587a392b6765464a72514e393141387572584d6357494e37775531534374447751684d6f70616f4262585a442b486c6d57496d6b437672427a796938624657685644746c4d444e4d47344a62396c573141564f34422f454b74417a64647741524c447634427a727541355172674c2b4a77494c7335516d4f677a30596241796d41413131446334754762545838396a333466656962416c3032324455466c7338334f304f677863593672326241674133464166656a76594c59693232453176624f33582f67383364687a324232317a6d4f56336d6b6b5479323951787943536430594a434b337042674b486b4d48684b6b306c654c57764474726b67586a464c693565737a3942782b67366b4633322f79304778614e4e4945732f737753654d343371574a696c4f384839376d36623979537a66764a6d4152674141434347414e474a47663150746e3732435461762b62714f716a43336975444632712b687933372b74435759647051424145515241455152436b33546552454559374c6f71374d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f39275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414267414141415941674d414141436447645672414141414446424d5645564d615846436866524368665243686654307443505a4141414141335253546c4d4167464a456b47784e414141414c306c455156523441575041447867647742543342544446394155697568644336574e4b2f762f2f2f792b55676772436c53413037455756676c6d4546774141356559534578654377696741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3131272c2764696d675f3231275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414649414141425343414d4141414477386e4f70414141415946424d5645582f2f2f3933476b6c3247556a6d3074794b4e324a39496c43494e46393548557a31376648372b5071424b46584f714c7a392b2f796556337672322b547a36652f446c61336276383678655a6152516d75565358436a59594c77342b7135686143454c6c716f615972486e62505875636d2b6a71657262343751726344697939636a4536764241414144756b6c455156525968653259323961794b6853474253564555544655464866336635632f696943536c562b4e64624a4737306b6a6841636d7a41306142442f39394e4e502f325052754e434b36616345767948764d7130752f7778597a6a744c2f34513371425746487848374b72562f73746b6777614c506b48534b3049344531667731556847686939544d6235416b5662766d496d4753666f636b556f31316b62426c38566449496c7331314558654a416d2b51524c5772754d63354b526439464d6b59516e306b5a76546634714d4d7742385a504157535568634e49304b56584953713339486b727958517356715656565a5051705a357551724a4733536a69634951794f4d576a354f78546262784a53475349384475474e61386755796c686b43436752324c662b5362467058576c52346b5832474e3758506b666d59484841574331717865484a546e5478556a354f6e794c7a475a3043396277767a7a38696965307063686b6e365a79526c79424b6833696667626750506734622f44526c794f7835463958325154497856757739454d696a71534f6d4754464d626166467a4a4758324a4b4d68584632636b714c5039715633684f616855726d5a44394551626a7048787257317348644370686e4e544a446e76717450723130394e33596a6565686c323245372b38673330564f3257377a775937326b6432774f6f6677724d754e61776774706153782f6853526e534a4a764b72795a552f5157535949317543376d53784b4f3731665a424568634b68516b7a7675686a717758506b655767537151394356534f5754597933764745777a322b486d4f6c4d486d5975644930737970364b70494a553177544574506b56536f7177456536436d53354f6c59335235676235426b564568594e536449476772755a2b424c794870424a76306a4d7062384c4c764261386a566367385a6938514871765769396e595243556269752f7141344a4547634d493756724b337271365273493739674c7a74524c6a517370475644626b53506152626b566c78524d616473385132593331596248483748726b3630534e79746f7545714376644634494c4d533431306a4f63375847337051416a2b52355a726b6a76654d686f7265364f784743346b6a62555879794f546d544c78455032337964376c64793267533679794179796e59394957796865707542742b3836525a71425a774f367572354771674236527074755755586269644873616b4b72726a6f544a736d47483437467543626d7a7a4a6a5a63465449336b50434b6a54544131415848704b4b2f5972484a33336d744f68724e2b794e6e5935466e4d3135735742687443376b344a64393469776e45326d355a48576e446577466e745237473235357661547a684433576e6959376844684b45504c7a4d4759615365394f493453334a554f63565568716738523042623677324459756462747531352f6745626d376b53396b64376b6a767176613063455a4d706a504c342b524d49652b567066566375636d2b684a4a792b7168546b436339633439793768584d54713332794e53767849413078694f4c58424e676a67532b584c412b72304249476263734243746d663641624d52646139674d436b672f526e627263464b4a6d5377426444656162474352307654554a3237626a5a796d63424c7256355436727371454a6c44623778437171756459582f7a596f74346634394f58783865656139645050776e39394e4e502f34332b41627851504c6a33385835594141414141456c46546b5375516d4343273b76617220693d5b2764696d675f3133275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d41414144785067523541414141636c424d564558754d794c2f2f2f2f74494144744a513334747250754d522f74496762306a346a326f3537744b5250744667442b2b666a326e706a796533502b39765838337433754c5272764f536a765244627550433334757258765545583936756e316c354c734141443731644c3372367636794d587861562f30683444367a63727858564c796347667857457a76537a2f35774c3378596c6a796447355a6e6968724141414745456c455156526f67653261375a616a4b424347465270554e49716d51324b4d332b6e37763855315569674b5a6e706d31396b357537352f2b675331486f47694b4d70326e454f4844683036644f6a51763667414c555578333564583970384c3362324f426a7343306164724b446b6873682f7777775336626848735272514433527a395a714162375558634173624f546f4f71674c6e3030534a577848496e563158413634324e776a3041377a754e71514a475750346d5349436a4d6b4a6677765051347247424f774531684231754e744c76414231306c773039612f315271627156583866664433727944586e3461546236747069314366545a616677626e6c555873657837644c50456970795646732b7a64644563306b51324e45674334786d5977477a2f493844726252783168487a3557374267583241566a614e2b682f36355462414e74437a646e7765754644486e4454434d6c6454394869766e786841612b2b3843512f48466e4733676241592f775061647a52594a675957564d46757773674772376e58724779435234686d597a6c2b4c46526f4a79313134304c715432346530542f6d506530696f4d70317070686c346e6476674e65766448496f7a2f7947517759703148357070656f4c474b31756a6c6b42526a6371564779546f5230436b2f4e4c546f69362f774b7757477a776a654163656d436e5a6532445167756c65343545415a6c56737067784770474641544735663734416b5662366f6d326146624179377a645450694b57457931454e7337644156494844364b5a5a424d507a5a5863594b3942526f394b73675651445472366f6d38594e4e50706245326a74495947352b57706764496936494e2f4577356f7636716b507a38446a716a65384751692b4e3832683237547937776c65686176667765434c594c725148515a47774256763036485a53386445483538564c2b7a4f7371733534764a4e594a4f6f4f6546674f694663785a64686c4e574b767543356352736f45716b7048417547495a44305a34596f493272664967524233684d323355574a6f437338654d326d7876514e304642457356725a636546485062695336794f734273454e703775465535754e626d745a484674414d517957476a6c646354614e37614b5a322f62446e7743477a79457444657251754843697a68354155592b75695a3969325236576731762b54614435644a686348556937672f4f6e33736d71485265394252682b47386766336c4c6c5634766f354e4345645634685242774c6b5565315843444749362b6e5347733258732b3268634878537348797451686d2b4a786c35344252767658496f4f452b55373938487470617849634f2f5235685a74567535536a734a315a39375558633269333271696e386a34476e2f3177502b334b3574625237526573705457544c6e555775437349483264446a685639614f57617150346b6a31443261356e6c6d36343074594b52746d73636c6d4f494430594d47665664743351515364726d4c4d55754e63382f5279774d42505658696c586d4534764d686b53544c5a2f582b56376f64716261415161706e4d364b634377536f3062504871734d6a634a6e667858317472536873413347397974632b564a366a4b6b655437596161774e634447314f385067484c7542303854515079334b4a4b6362504352324144756b6e36746f7152464570567a6164366944746b61387255654a536654684375454f6f6563535932344d617832374c7748787a4279546c76686b51764b36586c49636d666177623353307253467037395241434d723145552b644d78704c6564453231412f46536a474c7a794e5a724b5939516471544e384f446a6e717a44453543485a37626745436f516f5259796f3038377a65366e2b4138784f70537334326f717067314f684241373150674d67684176326b4a597153786e62426954796b466a53715a6f6c5061566863676146616963384857394e71414c434b366f536743564b32594279524d504f5355466348756d6a6d79786446456864534e4534326d4732416849755454586d6d453666456536544d706943574a4d4533615137684e6f464f63623165516c30626e4c614c51467a64635a2f69526c4c445a52547358486c7441624367662f446e45524c704448584e696a68573841795851484278766541314e38774b394157304673447764463973795a734151627962426d577035556171506a6e7a66704b6c36336e554d36365a2b594e4675425541574c4255687a4f766d4934524b36757249415949734a6c323073314949477053725449784d645868524c716f6b4936426b7979424b7276536647626861383773504b612b3030744b33595a69336845726e4d337270556c7a737258755a726f6b5959674469556c7978526167535144582b6a54327a4234394f5a4559546a577464566d474a654d3451437a573132356e327747336c366258486546636c5a6f32793673477a425664632b77754a616c583732736a5846782b6b5467697274586576667831394235745433705879324743375a553037376a30386f314e4e5a4a6739713845472f7368354731696d6b48456d34533551524e33306230563845326f4765766d6d376b4e4d52592f5155555866426c5659454c5332784a4d61724f2b726c794145497063753151424c57465a7252345476394c77476d70495957665973334e704a4a3769376279524e4c566f7a4c446f54684b7934387153664c4362357a4638356932555a4550562f7072693255326c7a346d3156324b4e33474f537472742b587a7753686b6f4d6a4e706a7448347458482b527847753653676948547030364e436851332b492f674a2f6948506855784b5a387741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3135275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141413156424d5645582f2f2f2f6a6641446a6567442b2f2f33696467442b2f6672686367447267514438367450366a774436376433392f5066376c41447468674431696744326a67443335395037394f763738655437307148706a525866625144716b437a3133735432343833686677446d6b555838324c50366f4450316c532f766d5437726b68623030375478794a767477492f737549626967436636776f58346e682f76304b726c6a546a6f6744583236642f77785a37686642726e706d3369677833747670546c6e316e6c70574c7172336a767a4c54706c32546e6a457a7670337a757771586f754a6278313862696e573770716f44706c46726b63786e707359336b6479626d6d555030723176347a4a4c367533583272453331707a6e796641416c4e7748774141414e4f6b6c455156526f67653161435865627568494749555143735931494d4574365578616e45474e6a77456e644e4c6b70625872372f332f536d7846657744464f2b3972653838343731546c3144466f2b7a66624e534b346b2f576c2f6d6954706a764c76417159732f6e634259325a6e36722b45705a704f6b6b526c3551572f46555a426d366e635361496f4468304c704d75383548666953524a33677a444b484175526c314f4657776c3166694f674730616861786e71536a47347362793965372f696269616276774e4b6b525365784a4659652f7268357537526c5054567a6430484b6244693334496f57566b5563496c7a2b4d72763339383833457553385948666378377965577239636a676c695630442f6b342f6d737370574f2b653636725264446d426c4e37785834786e785948424a5555314832372b2f6d67595169442b734a713647495768593651706c33346c366269527a706533793655424b452f4c6577752b54766e7138655a6d4b5a416a79357a4d6a562b495a325a63637039752f6e34414b62677133542f64664870554662363865586f537174526a3157585672304a554a434d7955484f3371395855584b71414b443038725a61415051554f554666674e30346b42617871614f46486d3272736b614d6942655953564b644d5665582b34656239453677364258633137724558465875376c494b466c4c487352385341565254446955732f54564f2f7142795541324d503133534d3230635142722f6533393138584973396658783457454a77726a37646656436c49414d6d2f333545574a6f48706363596f35544370383238644747756a6349742f75456a35782f76707841504b78302b5631504f7265586a337a667751706f7570616b687157456d56665148614e55745a45706b797168334d523566654d7932575435763071756c532b7239394f6e75426f4d4e325852353933674c2b6c773933433978543462705a4658783643564759627666475278475261684d364b514954554f465a7568426d524a477834474b4b6f5552717252386646677041516362547a393875734e34554131754a6d57614531414c4a64544f72474c6d66422b694a594e3065597955714268476f306f6556446c6a4261416c316e513152564c6a397838665079355269397930544366326852474954414253546f73713043635439377438315a68545670677730417a6e5256484d4936665256557a735757796f7a764c6d41373551514c59555a654d414e674555496f4e654743506a4b6a41744c756d754b6166485357367a47576353414942546778454a7754586b306855305855337373614d75507a6f636542706b57306d75365251455147515a4a5a756c5253783242794670756645386d666d39524f3434727237656a6d4c6f7742636c6f374973316b46513667665971382f424f4a6c6a38506550364a4f4b75386852697a434b73627859424d337975684e557151632b586c5330364b4e564d70756b666855366131413342654667475a764b6e6b647444424130494d696432677755646676707653475a3551785641426133535a3370584b774d6476426e464e2b4467744f616c54324151686a302f334673416c63795849616d6c646941345559467247485861466770385364317348726931687832497850687a346e516f384b64796f4d6c7945597a6146453250317a4a4351386a73676544714a2f46384a336d6d623774567430596c4a64487770526d4465487470417a5870537864514671436e5668426e446236625a7948356e355a5a566e655536334738364b2b386968722f47514738685549703570516b6f574e2f536f3070616952696b5179557970304d733461492b6978543344487370444c5330736f347a6a475645425a654241526d77484c627979775142724a636c7330576d656754726541674335637a6c4f663179674c545350554a444968452f71464c55304b384952326367706d374768394448566d56736f32556d39513037576e676a337a79704855424879504641587a4f5a71496f5249555351304b4559516757516f6875423936536b624a41566f3158644f4569712f784b46555059567255474754394151717264456b76684d6f4c6e734c6e584e6a4f4c46435473435566597643414b416f75394c4a6142616468784b766e433064764a716d7863417268414c5278426b595758457053384c7859435169424f67495330344b674c6d6b6148366d415935496e69643539742b594b614f6b3852426f4e476d334b746c66456933672b526a6f68794b62365975794471424e427a55614a5157446e432f306730726f5a637a7333793636755a6449347449416c525741347775666f4c425231764b71363155526f452b5253515a57576a7a7970566a614d6b6550584b444f344b6f776f366567625a524f71417852505a737a50664d51726d7a4a61444c55574535446172674e3834503745456d6f67496c526579516e6f536c62534b617a4b4f49497a5546586e744b454b4975646774424c4e704b7933696145497a6b396e4651686b2b5a6a52462f435938316678444e77536a77377241644a6f4261456f484b3851626d68594a684237737a3048765a546c4652786169472b7046534835556573317a635777304d6442377744754c487749725654486f45344b344473354c7a4a584646475a54443343434a59676754716e4e483431772f4b7342713752493538654f65594144356432444869574c33684863456770504e2b7462524763784450564f614842712f717357424942305537314e44326f6a653138437a626b354f766f4545514e365263316d3077457363774e55436b4162707054654634644748754c38594b526770656f564f36422f6a6331683246425357426961644275657472435130677952796e4e4259526c376b4a55554c6f7450335655425756374b755a7a34413861524d4b546e5a6d2f586439504a396977376c336e625253315a4774713230484f49714d3532467649526f51754e6b3463436b3244545a554f6e7544305764794d4356697838656c4e496f4d3932757969456c63456b726e6d377162794578555a556b2b77325359576c4e746a537967327838436d324153615752533132475735325542696232714f6c684249595153344558705355596e6c2f72794b36367277476b716c644f343258474241766f72567459534f344e3563654b496f594c4d696c367661677856537652454c2f73563270625941642b6f4476302f6e5a73556735775563366d48466764494a3868415269574d3246304b61516f556237634555396851466872736f2f43696569486f6e72584d303455374851496178514e7a774b476c68676d4e414a7471716a417450424f71454d5978556a6d6e57744c3159646c755559516d6c43474e6a483072773962364a434b2b7432786f5162736865776e5a55744b6141587550626a72546d584463555a346e41593432553873534459544e33742f756b71442f58496c74646c6339466d712f334c644c594267386a57325433744b35434b4a69686d55465531716d6f4d325235374a6c626c30326174586b6c6c4e5559414136656d36556b4d362f6e4f5661487773316d6564346b764265485954316c45435237784771345765566a535261466a754d45436251677159786d70795a6b4b747155675836727a6e564c767832316f7444304638464c796a596639555a6174524f7a687557416a39554146416175726b50524e747659533958424138453865627972354257706d70464f30424c6d69663439366b4f37624f6d47367934495a4f716362354b6c6f613831596b4174587252337937757370455232687947673673694f33797a6f77514943527751353549597143397a3167716f494f5967513072706a656e454d4d7964647644537a6a755a4a7330724a2b6b5167693978414356535969626d656f70666f664939746d62715153725848756f5630504339543175564d4d516e4f476e4a64786c41494d4b5152747569627265427070447435612f4365746b2f534f314f497542512b554279706c51713276397669754157372f747763457072513337773869756441475541365732356e79694f41614d645a366b4d5431774e6b632f4b43343246777a43497847344f564f3468302f446f6768634e313572696d446f514441524c45425a366e6f486e56676668744e33644771705475696167663878717347576764384937694651684d427a6a47354b2f39504d4342712b7439382f6366384646435173756675423958387a3054436f55647538576b6e754346677a72346a7173575657516a7236745463757a32792f2b357133456a6878716b5342685a6b306254614e3476346b39656a484e303745714a684950765774702f32763676726c4e62737a4754637367776534332f334c72484d562b2b2b71565837582f612f3262375152736648373435632f534d4f6a382f747a70394f727a5a6352322f684d645737776b386e6a514c4e6c39664c4b75494b514a5076317750627072686e702b6230764230654e6d5a64546b38506233657a7462666e5a362b32394b47496e71666d306a344446387644346877636746543849767a35757a30644c6462342f6b4d73553750686c3836347939507a3971414d4f6c646936663432646e5a6c57416e3638335a326274447565766b4c2b6941762f71625578693841377947523967687648767542355432416457764d45316f2f424b6d667a356754365542564b527a4242687541666b376741494a34632f587a6a365041307268734f6c57727358307778494b6c594a4570322b2b624731344d6b4c5a54416c322f4f616b50523441523063415964377050336945523145504d6e38624d4f784d504c7343306237412b70304566446b364b71454651434d41636c476a42354e514737436c6767336735664273314e454d7642683241643932317232472f6e4f7830654868484e5143484c595338516251484936473139327747413362586a6f616451477848397a736e2b466f644c67304f666c724e477741523375414977513865547361646c537a447a6a73416972577851676d5771505273443663764c65417346426270594f52414c7a385042792b365155456c513648585543706867484f35546363645a4339476b4246414c596c58414e2b67666544397333554869444d3367504541654831743948686f4e67434b732b48415a2f4434556872543330464550783041455a4d766f314750655551417236786f756774614c316c5a584d4e57434c416c355a7958675530726f624469355042384f74687641625130595a6f3552616c624143726b7776776d7462345142747062634442384f31652b523343694f543532376c307544574173487a582f5465416f54556544713561485945323048592f34756a6a7757425051736b5a444c546e685051567443642f44515a6a52394d47304671324d7545524152336c737a6277576c346a414c63624f41536f587730474638355833704e685432444b57502f7935545073712b3443347630386c36344249646831584c3471496337346c6a67393578774641532f575579396167466f4443446d66447251575356322b4b7145596374316236514b67686b426d48364144674e4865616c304a3932585259613766653237635347694f4e61304e364347674969574a4b512b3063696645433556714c77417438496a443139685349356c32434642754a4e5249634b567072534e6b6f476d76417372616f502f5132512b6f43554161786c547a646e6544416446494731445458746751414c556a67424e74432b6931415455425346695a554533626364424c77497344456d72392f77477141376a7a4c484d394b556b632f434670393139453967466c4d745a6a614e4675376d7541736a5a6541354979324f775741655653614e4c305a46707570516a7765726b4c364e68346f3746547245586b3434414541546e2b6173593278333246343732744c616a4838676c4a6577454a47627455396d537641796a334137707251476e426d6d7668426c434b6d4f77316c305a4741524a733577633273376658765a492b5933622b516b4a343767384c39784e4d77533971525a6d3975394177596e6755706c4f634d41793363617a44512b74734155384a666f54687a6f5947767579394e75415a39445a6654566971355848773246774a39303374613072727336667a5545632f7a7038542b702f32702f312f742f38412b724a44466c344139755541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3137275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645575a4141442f2f2f2b574141435a45684c7a354f535341414474323976713164585772713743686f61765656586e304e432f666e372b2b2f763538764b705330754e414143694f546e66774d446275626d5a4367717153456a323675713965586e52704b53474141446b794d6969516b4b36635847684e44544a6c4a537457566d644b69716149534764485232775947433161576b305454574c4141414376306c455156526f6765325759584f6a4942434764654e714c4e56456a4d6247746b6d622f502f665747424230574454752f4e6d626d37322f59514c374f4d43757842464c42614c78574b7857437757693856697356697366307962425230696d4a73414944544a39335a59374848434e416d7150554c547a6d785a2f626f787944642f5576734f6f7a63784759386859426d486c654d755945324f326b6c5654477744454d36547765304b77446a756c5074714f756e7369466a2f45664170334b465763427068584476484d4c5758693843796c764a6b42376c6d6a7057553076374f53526c623636626f34564c4c4f7445687044546e772f4a793353316f5746624c4c53774278517675477a744f4e63316135684168496e6b734b745838644f76566f65725a622f562f584d6c794a4d2b5971625a384a64767a486b4d42456a446252324158554a30736175626143566a677a767549552f32424769695177685947434f5a487a3065374a6148776667324975563331616744754f7a4a56777a2b30384c7761454b357545776367374f6959644b5a667539726962774450433043374f3855594964684e31716b495a6933376878466d716954495759537471545233533270505462735a675743585761556943724f2f6a34415267436d524870427331476d4250514b364c5978504f414b6a4379574f536b57546e506c6a6f4f5036774e46716765393533746b7a47736533385a53717543516470452f5535366634694659425469534e4a77654547316d504a6b4d6b7267394d71617734594252524b6c494e754d4c6177474a3767436b514b52564e5257763159566746574769565a646f3177385867674e434d4662746243316a6365715871416a6834475a63557831586f56774d3264374e486f4574464e64643872674c6366514e307165694b30393848326c5455536668546f436f6a376a355556364f37783754563359657a797733785252394f6f63786a4b756f455656507339625230487872506653704552754e4b49635357526a6171535365777949536f76542b2b6e49526f615843717468636f46585553536945536370514963566f737037506e556f6f42612b4c4e39743830543242547354334d58306a46556f687a59425943706a3751383675416b626b5664524a4f6763464831426f52556970576438446c434a736b3955585076706c56657341334d5a695478747939535772324749552f4a666a794a754a4d49665053684f453734476d4a78324b78574377576938566973566773466f7646597630332b67492b6453615a504562466d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3139275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b2866756e6374696f6e28297b76617220613d652c623d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e6e617669676174696f6e3b622626323d3d622e74797065262677696e646f772e70696e6728222f67656e5f3230343f63743d6261636b627574746f6e2665693d222b61293b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363743d7b7d3b676f6f676c652e637363742e70733d27414f765661773144704144365477704f5055674e344e6f72467065555c7832367573745c78336431353635353239363336323834373838273b7d2928293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363742e72643d747275653b7d2928293b7d2928293b2866756e6374696f6e28297b77696e646f772e78703d66756e6374696f6e2861297b66756e6374696f6e206528682c662c67297b72657475726e227870222b282278223d3d663f2263223a227822292b677d666f722876617220623d2f5c62787028787c6329285c643f295c622f3b613b297b76617220633d612e636c6173734e616d652c643d632e6d617463682862293b69662864297b612e636c6173734e616d653d632e7265706c61636528622c65293b6966282263223d3d645b315d29666f7228613d612e676574456c656d656e747342795461674e616d652822696d6722292c623d303b623c612e6c656e6774683b2b2b6229696628633d615b625d2c643d632e6765744174747269627574652822646174612d6c6c222929632e7372633d642c632e72656d6f76654174747269627574652822646174612d6c6c22293b627265616b7d613d612e706172656e74456c656d656e747d7d3b7d2928293b676f6f676c652e647274792626676f6f676c652e6472747928293b3c2f7363726970743e3c2f626f64793e3c2f68746d6c3e", - "rawHeaders": [ - "Content-Type", - "text/html; charset=ISO-8859-1", - "Date", - "Sat, 10 Aug 2019 01:21:31 GMT", - "Expires", - "-1", - "Cache-Control", - "private, max-age=0", - "P3P", - "CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"", - "Server", - "gws", - "X-XSS-Protection", - "0", - "X-Frame-Options", - "SAMEORIGIN", - "Set-Cookie", - "1P_JAR=2019-08-10-01; expires=Mon, 09-Sep-2019 01:21:31 GMT; path=/; domain=.google.com", - "Set-Cookie", - "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/complete/search; domain=.google.com; HttpOnly", - "Set-Cookie", - "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/search; domain=.google.com; HttpOnly", - "Set-Cookie", - "NID=188=vTMutucOBO-Yl5bpVtVnzkN1voOukQ24RkD0wuuzeNL_BDPMEB90MqBF06HFaILh_fs-PO8JGLhIjkSb3nxl9Rzf8L7CxJtk_yJF0aEgi2znY0rMT_dQr6_5tYfVNKU9u0d2BoXOVOWHEN3ZzaD7q6yRUb44yH3vjL0kue6Ki0s; expires=Sun, 09-Feb-2020 01:21:31 GMT; path=/; domain=.google.com; HttpOnly", - "Accept-Ranges", - "none", - "Vary", - "Accept-Encoding", - "Connection", - "close" - ], - "responseIsBinary": true - } -] diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-disable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-disable.test.ts deleted file mode 100644 index d3f4db8399..0000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/http-disable.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { NoopTracerProvider, NOOP_TRACER } from '@opentelemetry/api'; -import * as assert from 'assert'; -import * as http from 'http'; -import { AddressInfo } from 'net'; -import * as nock from 'nock'; -import * as sinon from 'sinon'; -import { plugin } from '../../src/http'; -import { httpRequest } from '../utils/httpRequest'; - -describe('HttpPlugin', () => { - let server: http.Server; - let serverPort = 0; - - describe('disable()', () => { - const provider = new NoopTracerProvider(); - before(() => { - nock.cleanAll(); - nock.enableNetConnect(); - - plugin.enable(http, provider); - // Ensure that http module is patched. - assert.strictEqual(http.Server.prototype.emit.__wrapped, true); - server = http.createServer((request, response) => { - response.end('Test Server Response'); - }); - - server.listen(serverPort); - server.once('listening', () => { - serverPort = (server.address() as AddressInfo).port; - }); - }); - - beforeEach(() => { - sinon.spy(NOOP_TRACER, 'startSpan'); - }); - - afterEach(() => { - sinon.restore(); - }); - - after(() => { - server.close(); - }); - describe('unpatch()', () => { - it('should not call provider methods for creating span', async () => { - plugin.disable(); - const testPath = '/incoming/unpatch/'; - - const options = { host: 'localhost', path: testPath, port: serverPort }; - - await httpRequest.get(options).then(result => { - assert.strictEqual( - (NOOP_TRACER.startSpan as sinon.SinonSpy).called, - false - ); - - assert.strictEqual(http.Server.prototype.emit.__wrapped, undefined); - }); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts deleted file mode 100644 index 310908abea..0000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ /dev/null @@ -1,843 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - SpanStatusCode, - context, - propagation, - Span as ISpan, - SpanKind, - getSpan, - setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpPlugin, plugin } from '../../src/http'; -import { Http, HttpPluginConfig } from '../../src/types'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import { ContextManager } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ClientRequest, IncomingMessage, ServerResponse } from 'http'; - -const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; - -let server: http.Server; -const serverPort = 22345; -const protocol = 'http'; -const hostname = 'localhost'; -const pathname = '/test'; -const serverName = 'my.server.name'; -const memoryExporter = new InMemorySpanExporter(); -const provider = new NodeTracerProvider(); -provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); -propagation.setGlobalPropagator(new DummyPropagation()); - -function doNock( - hostname: string, - path: string, - httpCode: number, - respBody: string, - times?: number -) { - const i = times || 1; - nock(`${protocol}://${hostname}`) - .get(path) - .times(i) - .reply(httpCode, respBody); -} - -export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -export const requestHookFunction = ( - span: ISpan, - request: ClientRequest | IncomingMessage -): void => { - span.setAttribute('custom request hook attribute', 'request'); -}; - -export const responseHookFunction = ( - span: ISpan, - response: IncomingMessage | ServerResponse -): void => { - span.setAttribute('custom response hook attribute', 'response'); -}; - -describe('HttpPlugin', () => { - let contextManager: ContextManager; - - beforeEach(() => { - contextManager = new AsyncHooksContextManager().enable(); - context.setGlobalContextManager(contextManager); - }); - - afterEach(() => { - context.disable(); - }); - - it('should return a plugin', () => { - assert.ok(plugin instanceof HttpPlugin); - }); - - it('should match version', () => { - assert.strictEqual(process.versions.node, plugin.version); - }); - - it(`moduleName should be ${protocol}`, () => { - assert.strictEqual(protocol, plugin.moduleName); - }); - - describe('enable()', () => { - describe('with bad plugin options', () => { - let pluginWithBadOptions: HttpPlugin; - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); - }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); - }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); - }, - }; - pluginWithBadOptions = new HttpPlugin( - plugin.component, - process.versions.node - ); - pluginWithBadOptions.enable(http, provider, config); - server = http.createServer((request, response) => { - response.end('Test Server Response'); - }); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - pluginWithBadOptions.disable(); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assertSpan(incomingSpan, SpanKind.SERVER, validations); - assertSpan(outgoingSpan, SpanKind.CLIENT, validations); - assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], - serverPort - ); - }); - }); - describe('with good plugin options', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - '/ignored/string', - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - requestHook: requestHookFunction, - responseHook: responseHookFunction, - serverName, - }; - plugin.enable(http, provider, config); - server = http.createServer((request, response) => { - if (request.url?.includes('/ignored')) { - provider.getTracer('test').startSpan('some-span').end(); - } - response.end('Test Server Response'); - }); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - plugin.disable(); - }); - - it(`${protocol} module should be patched`, () => { - assert.strictEqual(http.Server.prototype.emit.__wrapped, true); - }); - - it(`should not patch if it's not a ${protocol} module`, () => { - const httpNotPatched = new HttpPlugin( - plugin.component, - process.versions.node - ).enable({} as Http, provider, {}); - assert.strictEqual(Object.keys(httpNotPatched).length, 0); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}`, - { - headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', - }, - } - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - serverName, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], - '' - ); - assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], - serverPort - ); - [ - { span: incomingSpan, kind: SpanKind.SERVER }, - { span: outgoingSpan, kind: SpanKind.CLIENT }, - ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); - assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP - ); - assertSpan(span, kind, validations); - }); - }); - - const httpErrorCodes = [ - 400, - 401, - 403, - 404, - 429, - 501, - 503, - 504, - 500, - 505, - 597, - ]; - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; - - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - - const isReset = memoryExporter.getFinishedSpans().length === 0; - assert.ok(isReset); - - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - const reqSpan = spans[0]; - - assert.strictEqual(result.data, httpErrorCodes[i].toString()); - assert.strictEqual(spans.length, 1); - - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assertSpan(reqSpan, SpanKind.CLIENT, validations); - }); - } - - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - } - - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; - const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - await context.with(setSpan(context.active(), span), async () => { - for (let i = 0; i < num; i++) { - await httpRequest.get(`${protocol}://${hostname}${testPath}`); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); - assert.strictEqual( - span.context().traceId, - spans[i].spanContext.traceId - ); - } - span.end(); - const spans = memoryExporter.getFinishedSpans(); - // 5 child spans ended + 1 span (root) - assert.strictEqual(spans.length, 6); - }); - }); - - for (const ignored of ['string', 'function', 'regexp']) { - it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { - const testPath = `/ignored/${ignored}`; - - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - } - - for (const arg of ['string', {}, new Date()]) { - it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpRequest.get(arg); - } catch (error) { - // request has been made - // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); - } - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - }); - } - - for (const arg of [true, 1, false, 0, '']) { - it(`should not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpRequest.get(arg as any); - } catch (error) { - // request has been made - // nock throw - assert.ok( - error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') - ) > 0 - ); - } - const spans = memoryExporter.getFinishedSpans(); - // for this arg with don't provide trace. We pass arg to original method (http.get) - assert.strictEqual(spans.length, 0); - }); - } - - it('should have 1 ended span when request throw on bad "options" object', () => { - try { - http.request({ protocol: 'telnet' }); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request throw on bad "options" object', () => { - nock.cleanAll(); - nock.enableNetConnect(); - try { - http.request({ protocol: 'telnet' }); - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request is aborted', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .socketDelay(50) - .reply(200, ''); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - req.setTimeout(10, () => { - req.abort(); - reject('timeout'); - }); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length >= 6); - } - }); - - it('should have 1 ended span when request is aborted after receiving response', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .delay({ - body: 50, - }) - .replyWithFile(200, `${process.cwd()}/package.json`); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - req.abort(); - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length > 7); - } - }); - - it("should have 1 ended span when request doesn't listening response", done => { - nock.cleanAll(); - nock.enableNetConnect(); - const req = http.request(`${protocol}://${hostname}/`); - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - done(); - }); - req.end(); - }); - - it("should have 1 ended span when response is listened by using req.on('response')", done => { - const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); - const req = http.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], - 404 - ); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - done(); - }); - }); - req.end(); - }); - - it('custom attributes should show up on client and server spans', async () => { - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - - assert.strictEqual( - incomingSpan.attributes['custom request hook attribute'], - 'request' - ); - assert.strictEqual( - incomingSpan.attributes['custom response hook attribute'], - 'response' - ); - assert.strictEqual( - incomingSpan.attributes['span kind'], - SpanKind.CLIENT - ); - - assert.strictEqual( - outgoingSpan.attributes['custom request hook attribute'], - 'request' - ); - assert.strictEqual( - outgoingSpan.attributes['custom response hook attribute'], - 'response' - ); - assert.strictEqual( - outgoingSpan.attributes['span kind'], - SpanKind.CLIENT - ); - }); - - it('should not set span as active in context for outgoing request', done => { - assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { - assert.deepStrictEqual(getSpan(context.active()), undefined); - done(); - }); - }); - }); - - describe('with require parent span', () => { - beforeEach(done => { - memoryExporter.reset(); - plugin.enable(http, provider, {}); - server = http.createServer((request, response) => { - response.end('Test Server Response'); - }); - server.listen(serverPort, done); - }); - - afterEach(() => { - server.close(); - plugin.disable(); - }); - - it('should not trace without parent with options enabled (both client & server)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - - it('should not trace without parent with options enabled (client only)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - assert.strictEqual( - spans.every(span => span.kind === SpanKind.SERVER), - true - ); - }); - - it('should not trace without parent with options enabled (server only)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - assert.strictEqual( - spans.every(span => span.kind === SpanKind.CLIENT), - true - ); - }); - - it('should trace with parent with both requireParent options enabled', done => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const tracer = provider.getTracer('default'); - const span = tracer.startSpan('parentSpan', { - kind: SpanKind.INTERNAL, - }); - context.with(setSpan(context.active(), span), () => { - httpRequest - .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then(result => { - span.end(); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== - undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== - undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 2); - assert.strictEqual( - spans.filter(span => span.kind === SpanKind.CLIENT).length, - 1 - ); - assert.strictEqual( - spans.filter(span => span.kind === SpanKind.INTERNAL).length, - 1 - ); - return done(); - }) - .catch(done); - }); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts deleted file mode 100644 index be05279d9a..0000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { context, SpanKind } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import * as assert from 'assert'; -import axios, { AxiosResponse } from 'axios'; -import * as got from 'got'; -import * as http from 'http'; -import { Socket } from 'net'; -import * as nock from 'nock'; -import * as path from 'path'; -import * as request from 'request-promise-native'; -import * as superagent from 'superagent'; -import * as url from 'url'; -import { plugin } from '../../src/http'; -import { HttpPluginConfig } from '../../src/types'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { customAttributeFunction } from './http-enable.test'; - -const memoryExporter = new InMemorySpanExporter(); -const protocol = 'http'; - -describe('Packages', () => { - let mockServerPort = 0; - let mockServer: http.Server; - const sockets: Array = []; - before(done => { - mockServer = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - }); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - describe('get', () => { - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - applyCustomAttributesOnSpan: customAttributeFunction, - }; - plugin.enable(http, provider, config); - }); - - after(() => { - // back to normal - nock.cleanAll(); - nock.enableNetConnect(); - }); - - let resHeaders: http.IncomingHttpHeaders; - [ - { name: 'axios', httpPackage: axios }, //keep first - { name: 'superagent', httpPackage: superagent }, - { name: 'got', httpPackage: { get: (url: string) => got(url) } }, - { - name: 'request', - httpPackage: { get: (url: string) => request(url) }, - }, - ].forEach(({ name, httpPackage }) => { - it(`should create a span for GET requests and add propagation headers by using ${name} package`, async () => { - if (process.versions.node.startsWith('12') && name === 'got') { - // got complains with nock and node version 12+ - // > RequestError: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type function - // so let's make a real call - nock.cleanAll(); - nock.enableNetConnect(); - } else { - nock.load(path.join(__dirname, '../', '/fixtures/google.json')); - } - - const urlparsed = url.parse( - `${protocol}://localhost:${mockServerPort}/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8` - ); - const result = await httpPackage.get(urlparsed.href!); - if (!resHeaders) { - const res = result as AxiosResponse<{}>; - resHeaders = res.headers; - } - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: urlparsed.hostname!, - httpStatusCode: 200, - httpMethod: 'GET', - pathname: urlparsed.pathname!, - path: urlparsed.path, - resHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - - switch (name) { - case 'axios': - assert.ok( - result.request._headers[DummyPropagation.TRACE_CONTEXT_KEY] - ); - assert.ok( - result.request._headers[DummyPropagation.SPAN_CONTEXT_KEY] - ); - break; - case 'got': - case 'superagent': - break; - default: - break; - } - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts deleted file mode 100644 index c14ee4bbb7..0000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - SpanAttributes, - SpanStatusCode, - ROOT_CONTEXT, - SpanKind, - TraceFlags, -} from '@opentelemetry/api'; -import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { IncomingMessage, ServerResponse } from 'http'; -import * as sinon from 'sinon'; -import * as url from 'url'; -import { IgnoreMatcher } from '../../src/types'; -import * as utils from '../../src/utils'; - -describe('Utility', () => { - describe('parseResponseStatus()', () => { - it('should return ERROR code by default', () => { - const status = utils.parseResponseStatus( - (undefined as unknown) as number - ); - assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); - }); - - it('should return OK for Success HTTP status code', () => { - for (let index = 100; index < 400; index++) { - const status = utils.parseResponseStatus(index); - assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); - } - }); - - it('should not return OK for Bad HTTP status code', () => { - for (let index = 400; index <= 600; index++) { - const status = utils.parseResponseStatus(index); - assert.notStrictEqual(status.code, SpanStatusCode.OK); - } - }); - }); - describe('hasExpectHeader()', () => { - it('should throw if no option', () => { - try { - utils.hasExpectHeader('' as http.RequestOptions); - assert.fail(); - } catch (ignore) {} - }); - - it('should not throw if no headers', () => { - const result = utils.hasExpectHeader({} as http.RequestOptions); - assert.strictEqual(result, false); - }); - - it('should return true on Expect (no case sensitive)', () => { - for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { - const result = utils.hasExpectHeader({ - headers, - } as http.RequestOptions); - assert.strictEqual(result, true); - } - }); - }); - - describe('getRequestInfo()', () => { - it('should get options object', () => { - const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; - const urlParsed = url.parse(webUrl); - const urlParsedWithoutPathname = { - ...urlParsed, - pathname: undefined, - }; - const whatWgUrl = new url.URL(webUrl); - for (const param of [ - webUrl, - urlParsed, - urlParsedWithoutPathname, - whatWgUrl, - ]) { - const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); - assert.strictEqual(result.optionsParsed.protocol, 'http:'); - assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); - assert.strictEqual(result.pathname, '/aPath'); - assert.strictEqual(result.origin, 'http://google.fr'); - } - }); - }); - - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); - assert.strictEqual(answer2, false); - }); - - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); - assert.strictEqual(answer2, false); - }); - - it('should throw if type is unknown', () => { - try { - utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); - assert.fail(); - } catch (error) { - assert.strictEqual(error instanceof TypeError, true); - } - }); - - it('function pattern', () => { - const answer1 = utils.satisfiesPattern( - '/test/home', - (url: string) => url === '/test/home' - ); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern( - '/test/home', - (url: string) => url !== '/test/home' - ); - assert.strictEqual(answer2, false); - }); - }); - - describe('isIgnored()', () => { - beforeEach(() => { - sinon.spy(utils, 'satisfiesPattern'); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('/test/1', ['/test/11']); - assert.strictEqual(answer1, false); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 1 - ); - }); - - it('should call isSatisfyPattern, match for function', () => { - const answer1 = utils.isIgnored('/test/1', [ - url => url.endsWith('/test/1'), - ]); - assert.strictEqual(answer1, true); - }); - - it('should not re-throw when function throws an exception', () => { - const onException = (e: Error) => { - // Do Nothing - }; - for (const callback of [undefined, onException]) { - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - () => { - throw new Error('test'); - }, - ], - callback - ) - ); - } - }); - - it('should call onException when function throws an exception', () => { - const onException = sinon.spy(); - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - () => { - throw new Error('test'); - }, - ], - onException - ) - ); - assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); - }); - - it('should not call isSatisfyPattern', () => { - utils.isIgnored('/test/1', []); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 0 - ); - }); - - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('/test/1', []); - assert.strictEqual(answer1, false); - }); - - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('/test/1', undefined); - assert.strictEqual(answer2, false); - }); - }); - - describe('getAbsoluteUrl()', () => { - it('should return absolute url with localhost', () => { - const path = '/test/1'; - const result = utils.getAbsoluteUrl(url.parse(path), {}); - assert.strictEqual(result, `http://localhost${path}`); - }); - it('should return absolute url', () => { - const absUrl = 'http://www.google/test/1?query=1'; - const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); - assert.strictEqual(result, absUrl); - }); - it('should return default url', () => { - const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, 'http://localhost/'); - }); - it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { - const result = utils.getAbsoluteUrl( - { path: '/helloworld', port: 8080 }, - {} - ); - assert.strictEqual(result, 'http://localhost:8080/helloworld'); - }); - }); - - describe('setSpanWithError()', () => { - it('should have error attributes', () => { - const errorMessage = 'test error'; - for (const obj of [undefined, { statusCode: 400 }]) { - const span = new Span( - new BasicTracerProvider().getTracer('default'), - ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, - SpanKind.INTERNAL - ); - /* tslint:disable-next-line:no-any */ - utils.setSpanWithError(span, new Error(errorMessage), obj as any); - const attributes = span.attributes; - assert.strictEqual( - attributes[HttpAttribute.HTTP_ERROR_MESSAGE], - errorMessage - ); - assert.ok(attributes[HttpAttribute.HTTP_ERROR_NAME]); - } - }); - }); - - describe('isValidOptionsType()', () => { - ['', false, true, 1, 0, []].forEach(options => { - it(`should return false with the following value: ${JSON.stringify( - options - )}`, () => { - assert.strictEqual(utils.isValidOptionsType(options), false); - }); - }); - for (const options of ['url', url.parse('http://url.com'), {}]) { - it(`should return true with the following value: ${JSON.stringify( - options - )}`, () => { - assert.strictEqual(utils.isValidOptionsType(options), true); - }); - } - }); - - describe('getIncomingRequestAttributesOnResponse()', () => { - it('should correctly parse the middleware stack if present', () => { - const request = { - __ot_middlewares: ['/test', '/toto', '/'], - socket: {}, - } as IncomingMessage & { __ot_middlewares?: string[] }; - const response = {} as ServerResponse; - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], '/test/toto'); - }); - - it('should succesfully process without middleware stack', () => { - const request = { socket: {} } as IncomingMessage; - const response = {} as ServerResponse; - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], undefined); - }); - }); - - // Verify the key in the given attributes is set to the given value, - // and that no other HTTP Content Length attributes are set. - function verifyValueInAttributes( - attributes: SpanAttributes, - key: string | undefined, - value: number - ) { - const httpAttributes = [ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, - ]; - - for (const attr of httpAttributes) { - if (attr === key) { - assert.strictEqual(attributes[attr], value); - } else { - assert.strictEqual(attributes[attr], undefined); - } - } - } - - describe('setRequestContentLengthAttributes()', () => { - it('should set request content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - - request.headers = { - 'content-length': '1200', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set request content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - request.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set request content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - request.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, - 1200 - ); - }); - }); - - describe('setResponseContentLengthAttributes()', () => { - it('should set response content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - }; - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set response content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', - }; - - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set response content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', - }; - - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - 1200 - ); - }); - - it('should set no attributes with no content-length header', () => { - const attributes: SpanAttributes = {}; - const message = {} as IncomingMessage; - - message.headers = { - 'content-encoding': 'gzip', - }; - utils.setResponseContentLengthAttribute(message, attributes); - - verifyValueInAttributes(attributes, undefined, 1200); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts deleted file mode 100644 index 65b0040eab..0000000000 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanKind, Span, context } from '@opentelemetry/api'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as url from 'url'; -import { plugin } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { HttpPluginConfig } from '../../src/types'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; -import { sendRequestTwice } from '../utils/rawRequest'; -const protocol = 'http'; -const serverPort = 32345; -const hostname = 'localhost'; -const memoryExporter = new InMemorySpanExporter(); - -export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpPlugin Integration tests', () => { - let mockServerPort = 0; - let mockServer: http.Server; - const sockets: Array = []; - before(done => { - mockServer = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - }); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - memoryExporter.reset(); - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - describe('enable()', () => { - before(function (done) { - // mandatory - if (process.env.CI) { - done(); - return; - } - - utils.checkInternet(isConnected => { - if (!isConnected) { - this.skip(); - // don't disturb people - } - done(); - }); - }); - - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const ignoreConfig = [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, - }; - try { - plugin.disable(); - } catch (e) {} - plugin.enable(http, provider, config); - }); - - after(() => { - plugin.disable(); - }); - - it('should create a rootSpan for GET requests and add propagation headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - `${protocol}://localhost:${mockServerPort}/?query=test` - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`) - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), - { - headers: { 'x-foo': 'foo' }, - } - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); - assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP - ); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('custom attributes should show up on client spans', async () => { - const result = await httpRequest.get( - `${protocol}://localhost:${mockServerPort}/` - ); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = Object.assign( - { headers: { Expect: '100-continue' } }, - url.parse(`${protocol}://localhost:${mockServerPort}/`) - ); - - const result = await httpRequest.get(options); - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, - ]) { - it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( - headers - )}`, done => { - let validations: { - hostname: string; - httpStatusCode: number; - httpMethod: string; - pathname: string; - reqHeaders: http.OutgoingHttpHeaders; - resHeaders: http.IncomingHttpHeaders; - }; - let data = ''; - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = { headers }; - const req = http.get( - `${protocol}://localhost:${mockServerPort}/`, - options, - (resp: http.IncomingMessage) => { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - validations = { - hostname: 'localhost', - httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', - resHeaders: resp.headers, - /* tslint:disable:no-any */ - reqHeaders: (res.req as any).getHeaders - ? (res.req as any).getHeaders() - : (res.req as any)._headers, - /* tslint:enable:no-any */ - }; - }); - } - ); - - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.ok(data); - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - done(); - }); - }); - } - - it('should work for multiple active requests in keep-alive mode', async () => { - await sendRequestTwice(hostname, mockServerPort); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s: any) => s.kind === SpanKind.SERVER); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts deleted file mode 100644 index d6a700549b..0000000000 --- a/packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - Context, - TextMapPropagator, - TraceFlags, - getSpanContext, - setSpanContext, -} from '@opentelemetry/api'; -import * as http from 'http'; - -export class DummyPropagation implements TextMapPropagator { - static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; - static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; - extract(context: Context, carrier: http.OutgoingHttpHeaders) { - const extractedSpanContext = { - traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, - spanId: DummyPropagation.SPAN_CONTEXT_KEY, - traceFlags: TraceFlags.SAMPLED, - isRemote: true, - }; - if (extractedSpanContext.traceId && extractedSpanContext.spanId) { - return setSpanContext(context, extractedSpanContext); - } - return context; - } - inject(context: Context, headers: { [custom: string]: string }): void { - const spanContext = getSpanContext(context); - if (!spanContext) return; - headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; - headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; - } - fields(): string[] { - return [ - DummyPropagation.TRACE_CONTEXT_KEY, - DummyPropagation.SPAN_CONTEXT_KEY, - ]; - } -} diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts deleted file mode 100644 index 4db2a99b27..0000000000 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { SpanKind, SpanStatus } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { - GeneralAttribute, - HttpAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as utils from '../../src/utils'; -import { DummyPropagation } from './DummyPropagation'; - -export const assertSpan = ( - span: ReadableSpan, - kind: SpanKind, - validations: { - httpStatusCode: number; - httpMethod: string; - resHeaders: http.IncomingHttpHeaders; - hostname: string; - pathname: string; - reqHeaders?: http.OutgoingHttpHeaders; - path?: string | null; - forceStatus?: SpanStatus; - serverName?: string; - component: string; - } -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], - span.status.message - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], - validations.httpMethod - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], - validations.path || validations.pathname - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], - validations.httpStatusCode - ); - - assert.strictEqual(span.links.length, 0); - assert.strictEqual(span.events.length, 0); - - assert.deepStrictEqual( - span.status, - validations.forceStatus || - utils.parseResponseStatus(validations.httpStatusCode) - ); - - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); - - if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; - if (userAgent) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], - userAgent - ); - } - } - - if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders['content-length']) { - const contentLength = Number(validations.resHeaders['content-length']); - - if ( - validations.resHeaders['content-encoding'] && - validations.resHeaders['content-encoding'] !== 'identity' - ) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], - contentLength - ); - } else { - assert.strictEqual( - span.attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED - ], - contentLength - ); - } - } - - assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], - validations.hostname, - 'must be consistent (PEER_NAME and hostname)' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], - 'must have PEER_IP' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], - 'must have PEER_PORT' - ); - assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string - ) > -1, - 'must be consistent' - ); - } - if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders['content-length']) { - const contentLength = validations.reqHeaders['content-length']; - - if ( - validations.reqHeaders['content-encoding'] && - validations.reqHeaders['content-encoding'] !== 'identity' - ) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], - contentLength - ); - } else { - assert.strictEqual( - span.attributes[ - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED - ], - contentLength - ); - } - } - - if (validations.serverName) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], - validations.serverName, - ' must have serverName attribute' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], - 'must have HOST_PORT' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], - 'must have HOST_IP' - ); - } - assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); - } else if (validations.reqHeaders) { - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - } -}; diff --git a/packages/opentelemetry-plugin-http/test/utils/httpRequest.ts b/packages/opentelemetry-plugin-http/test/utils/httpRequest.ts deleted file mode 100644 index f507b7f1f1..0000000000 --- a/packages/opentelemetry-plugin-http/test/utils/httpRequest.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import * as http from 'http'; -import { URL } from 'url'; - -type GetResult = Promise<{ - data: string; - statusCode: number | undefined; - resHeaders: http.IncomingHttpHeaders; - reqHeaders: http.OutgoingHttpHeaders; - method: string | undefined; -}>; - -function get(input: string | URL, options?: http.RequestOptions): GetResult; -function get(input: http.RequestOptions): GetResult; -function get(input: any, options?: any): GetResult { - return new Promise((resolve, reject) => { - // eslint-disable-next-line prefer-const - let req: http.ClientRequest; - - function onGetResponseCb(resp: http.IncomingMessage): void { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve({ - data, - statusCode: res.statusCode, - reqHeaders: req.getHeaders ? req.getHeaders() : (req as any)._headers, - resHeaders: res.headers, - method: res.req.method, - }); - }); - resp.on('error', err => { - reject(err); - }); - } - req = - options != null - ? http.get(input, options, onGetResponseCb) - : http.get(input, onGetResponseCb); - req.on('error', err => { - reject(err); - }); - return req; - }); -} - -export const httpRequest = { - get, -}; diff --git a/packages/opentelemetry-plugin-http/test/utils/rawRequest.ts b/packages/opentelemetry-plugin-http/test/utils/rawRequest.ts deleted file mode 100644 index c2fefa284a..0000000000 --- a/packages/opentelemetry-plugin-http/test/utils/rawRequest.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import * as net from 'net'; - -// used to reproduce this issue: -// https://github.com/open-telemetry/opentelemetry-js/pull/1939 -export async function sendRequestTwice( - host: string, - port: number -): Promise { - return new Promise((resolve, reject) => { - const request = 'GET /raw HTTP/1.1\n\n'; - const socket = net.createConnection({ host, port }, () => { - socket.write(`${request}${request}`, err => { - if (err) reject(err); - }); - }); - socket.on('data', data => { - // since it's <1kb size, we expect both responses to come in a single chunk - socket.destroy(); - resolve(data); - }); - socket.on('error', err => reject(err)); - }); -} diff --git a/packages/opentelemetry-plugin-http/test/utils/utils.ts b/packages/opentelemetry-plugin-http/test/utils/utils.ts deleted file mode 100644 index 5d2e5cb3bc..0000000000 --- a/packages/opentelemetry-plugin-http/test/utils/utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import * as dns from 'dns'; - -export const checkInternet = (cb: (isConnected: boolean) => void) => { - dns.lookup('google.com', err => { - if (err && err.code === 'ENOTFOUND') { - cb(false); - } else { - cb(true); - } - }); -}; diff --git a/packages/opentelemetry-plugin-http/tsconfig.json b/packages/opentelemetry-plugin-http/tsconfig.json deleted file mode 100644 index a6187d76f2..0000000000 --- a/packages/opentelemetry-plugin-http/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "build" - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "references": [ - { - "path": "../opentelemetry-context-async-hooks" - }, - { - "path": "../opentelemetry-core" - }, - { - "path": "../opentelemetry-node" - }, - { - "path": "../opentelemetry-semantic-conventions" - }, - { - "path": "../opentelemetry-tracing" - } - ] -} diff --git a/packages/opentelemetry-plugin-https/.eslintignore b/packages/opentelemetry-plugin-https/.eslintignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/packages/opentelemetry-plugin-https/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/opentelemetry-plugin-https/.eslintrc.js b/packages/opentelemetry-plugin-https/.eslintrc.js deleted file mode 100644 index f726f3becb..0000000000 --- a/packages/opentelemetry-plugin-https/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - "env": { - "mocha": true, - "node": true - }, - ...require('../../eslint.config.js') -} diff --git a/packages/opentelemetry-plugin-https/.npmignore b/packages/opentelemetry-plugin-https/.npmignore deleted file mode 100644 index 9505ba9450..0000000000 --- a/packages/opentelemetry-plugin-https/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/coverage -/doc -/test diff --git a/packages/opentelemetry-plugin-https/LICENSE b/packages/opentelemetry-plugin-https/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/packages/opentelemetry-plugin-https/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - 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/packages/opentelemetry-plugin-https/README.md b/packages/opentelemetry-plugin-https/README.md deleted file mode 100644 index d4dfd1bde8..0000000000 --- a/packages/opentelemetry-plugin-https/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# OpenTelemetry HTTPS Instrumentation for Node.js - -[![NPM Published Version][npm-img]][npm-url] -[![dependencies][dependencies-image]][dependencies-url] -[![devDependencies][devDependencies-image]][devDependencies-url] -[![Apache License][license-image]][license-image] - -This module provides automatic instrumentation for [`https`](http://nodejs.org/api/https.html). - -For automatic instrumentation see the -[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node) package. - -## Installation - -```bash -npm install --save @opentelemetry/plugin-https -``` - -## Usage - -OpenTelemetry HTTPS 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 plugin (HTTPS in this case), specify it in the Node Tracer's configuration. - -```js -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider({ - plugins: { - https: { - enabled: true, - // You may use a package name or absolute path to the file. - path: '@opentelemetry/plugin-https', - // https plugin options - } - } -}); -``` - -To load all of the [supported plugins](https://github.com/open-telemetry/opentelemetry-js#plugins), use below approach. Each plugin is only loaded when the module that it patches is loaded; in other words, there is no computational overhead for listing plugins for unused modules. - -```js -const { NodeTracerProvider } = require('@opentelemetry/node'); - -const provider = new NodeTracerProvider(); -``` - -See [examples/https](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/https) for a short example. - -### Https Plugin Options - -Https plugin has few options available to choose from. You can set the following: - -| Options | Type | Description | -| ------- | ---- | ----------- | -| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes | -| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths | -| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls | - -## 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 -[dependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-https -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-https -[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js.svg?path=packages%2Fopentelemetry-plugin-https&type=dev -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-https&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-https -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-https.svg diff --git a/packages/opentelemetry-plugin-https/package.json b/packages/opentelemetry-plugin-https/package.json deleted file mode 100644 index 83b96be027..0000000000 --- a/packages/opentelemetry-plugin-https/package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "@opentelemetry/plugin-https", - "version": "0.18.0", - "description": "OpenTelemetry https automatic instrumentation package.", - "main": "build/src/index.js", - "types": "build/src/index.d.ts", - "repository": "open-telemetry/opentelemetry-js", - "scripts": { - "compile": "tsc --build", - "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", - "tdd": "npm run test -- --watch-extensions ts --watch", - "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" - }, - "keywords": [ - "opentelemetry", - "https", - "nodejs", - "tracing", - "profiling", - "plugin" - ], - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - }, - "files": [ - "build/src/**/*.js", - "build/src/**/*.js.map", - "build/src/**/*.d.ts", - "doc", - "LICENSE", - "README.md" - ], - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@types/got": "9.6.11", - "@types/mocha": "8.2.2", - "@types/node": "14.14.35", - "@types/request-promise-native": "1.0.17", - "@types/semver": "7.3.4", - "@types/shimmer": "1.0.1", - "@types/sinon": "9.0.11", - "@types/superagent": "4.1.10", - "axios": "0.21.1", - "codecov": "3.8.1", - "got": "9.6.0", - "gts": "3.1.0", - "mocha": "7.2.0", - "nock": "12.0.3", - "nyc": "15.1.0", - "request": "2.88.2", - "request-promise-native": "1.0.9", - "rimraf": "3.0.2", - "sinon": "9.2.4", - "superagent": "6.1.0", - "ts-mocha": "8.0.0", - "ts-node": "9.1.1", - "typescript": "4.2.3" - }, - "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/plugin-http": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "semver": "^7.1.3", - "shimmer": "^1.2.1" - } -} diff --git a/packages/opentelemetry-plugin-https/src/https.ts b/packages/opentelemetry-plugin-https/src/https.ts deleted file mode 100644 index 1b31b2178e..0000000000 --- a/packages/opentelemetry-plugin-https/src/https.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { diag } from '@opentelemetry/api'; -import { HttpPlugin, Func, HttpRequestArgs } from '@opentelemetry/plugin-http'; -import type * as http from 'http'; -import type * as https from 'https'; -import { URL } from 'url'; -import * as semver from 'semver'; -import * as shimmer from 'shimmer'; - -/** - * Https instrumentation plugin for Opentelemetry - */ -export class HttpsPlugin extends HttpPlugin { - /** Constructs a new HttpsPlugin instance. */ - constructor(readonly version: string) { - super('https', version); - } - - /** - * Patches HTTPS incoming and outcoming request functions. - */ - protected patch() { - diag.debug('applying patch to %s@%s', this.moduleName, this.version); - - if ( - this._moduleExports && - this._moduleExports.Server && - this._moduleExports.Server.prototype - ) { - shimmer.wrap( - this._moduleExports.Server.prototype, - 'emit', - this._getPatchIncomingRequestFunction() - ); - } else { - diag.error( - 'Could not apply patch to %s.emit. Interface is not as expected.', - this.moduleName - ); - } - - shimmer.wrap( - this._moduleExports, - 'request', - this._getPatchHttpsOutgoingRequestFunction() - ); - - // In Node 8-12, http.get calls a private request method, therefore we patch it - // here too. - if (semver.satisfies(this.version, '>=8.0.0')) { - shimmer.wrap( - this._moduleExports, - 'get', - this._getPatchHttpsOutgoingGetFunction(this._moduleExports.request) - ); - } - - return this._moduleExports; - } - - /** Patches HTTPS outgoing requests */ - private _getPatchHttpsOutgoingRequestFunction() { - return (original: Func): Func => { - const plugin = this; - return function httpsOutgoingRequest( - options: https.RequestOptions | string | URL, - ...args: HttpRequestArgs - ): http.ClientRequest { - // Makes sure options will have default HTTPS parameters - if (typeof options === 'object' && !(options instanceof URL)) { - options = Object.assign({}, options); - plugin._setDefaultOptions(options); - } - return plugin._getPatchOutgoingRequestFunction()(original)( - options, - ...args - ); - }; - }; - } - - private _setDefaultOptions(options: https.RequestOptions) { - options.protocol = options.protocol || 'https:'; - options.port = options.port || 443; - options.agent = options.agent || this._moduleExports.globalAgent; - } - - /** Patches HTTPS outgoing get requests */ - private _getPatchHttpsOutgoingGetFunction( - clientRequest: ( - options: http.RequestOptions | string | URL, - ...args: HttpRequestArgs - ) => http.ClientRequest - ) { - return (original: Func): Func => { - return function httpsOutgoingRequest( - options: https.RequestOptions | string | URL, - ...args: HttpRequestArgs - ): http.ClientRequest { - return plugin._getPatchOutgoingGetFunction(clientRequest)(original)( - options, - ...args - ); - }; - }; - } - - /** Unpatches all HTTPS patched function. */ - protected unpatch(): void { - if ( - this._moduleExports && - this._moduleExports.Server && - this._moduleExports.Server.prototype - ) { - shimmer.unwrap(this._moduleExports.Server.prototype, 'emit'); - } - shimmer.unwrap(this._moduleExports, 'request'); - if (semver.satisfies(this.version, '>=8.0.0')) { - shimmer.unwrap(this._moduleExports, 'get'); - } - } -} - -export const plugin = new HttpsPlugin(process.versions.node); diff --git a/packages/opentelemetry-plugin-https/src/index.ts b/packages/opentelemetry-plugin-https/src/index.ts deleted file mode 100644 index 0c7c3d80af..0000000000 --- a/packages/opentelemetry-plugin-https/src/index.ts +++ /dev/null @@ -1,17 +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. - */ - -export * from './https'; diff --git a/packages/opentelemetry-plugin-https/src/version.ts b/packages/opentelemetry-plugin-https/src/version.ts deleted file mode 100644 index 6fdb996caa..0000000000 --- a/packages/opentelemetry-plugin-https/src/version.ts +++ /dev/null @@ -1,18 +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. - */ - -// this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; diff --git a/packages/opentelemetry-plugin-https/test/fixtures/google.json b/packages/opentelemetry-plugin-https/test/fixtures/google.json deleted file mode 100644 index 550bb764b3..0000000000 --- a/packages/opentelemetry-plugin-https/test/fixtures/google.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "scope": "https://www.google.com", - "method": "GET", - "path": "/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8", - "body": "", - "status": 200, - "response": "3c21646f63747970652068746d6c3e3c68746d6c206c616e673d22656e2d4341223e3c686561643e3c6d65746120636861727365743d225554462d38223e3c6d65746120636f6e74656e743d222f696d616765732f6272616e64696e672f676f6f676c65672f31782f676f6f676c65675f7374616e646172645f636f6c6f725f31323864702e706e6722206974656d70726f703d22696d616765223e3c7469746c653e6178696f73202d20476f6f676c65205365617263683c2f7469746c653e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d77696e646f772e706572666f726d616e63653b77696e646f772e73746172743d286e65772044617465292e67657454696d6528293b613a7b76617220623d77696e646f773b69662861297b76617220633d612e74696d696e673b69662863297b76617220643d632e6e617669676174696f6e53746172742c653d632e726573706f6e736553746172743b696628653e642626653c3d77696e646f772e7374617274297b77696e646f772e73746172743d653b622e777372743d652d643b627265616b20617d7d612e6e6f77262628622e777372743d4d6174682e666c6f6f7228612e6e6f77282929297d7d77696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d3b676f6f676c652e6166743d66756e6374696f6e2866297b662e7365744174747269627574652822646174612d696d6c222c2b6e65772044617465297d3b7d292e63616c6c2874686973293b2866756e6374696f6e28297b76617220633d5b5d2c653d303b77696e646f772e70696e673d66756e6374696f6e2862297b2d313d3d622e696e6465784f662822267a782229262628622b3d22267a783d222b286e65772044617465292e67657454696d652829293b76617220613d6e657720496d6167652c643d652b2b3b635b645d3d613b612e6f6e6572726f723d612e6f6e6c6f61643d612e6f6e61626f72743d66756e6374696f6e28297b64656c65746520635b645d7d3b612e7372633d627d3b7d292e63616c6c2874686973293b3c2f7363726970743e3c7374796c653e626f64797b6d617267696e3a30206175746f3b6d61782d77696474683a37333670783b70616464696e673a30203870787d617b636f6c6f723a233139363744323b746578742d6465636f726174696f6e3a6e6f6e653b7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c2e31297d613a766973697465647b636f6c6f723a233442313141387d613a686f7665727b746578742d6465636f726174696f6e3a756e6465726c696e657d696d677b626f726465723a307d68746d6c7b666f6e742d66616d696c793a526f626f746f2c48656c7665746963614e6575652c417269616c2c73616e732d73657269663b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b746578742d73697a652d61646a7573743a313030253b636f6c6f723a233343343034333b776f72642d777261703a627265616b2d776f72643b6261636b67726f756e642d636f6c6f723a236666667d2e625273576e637b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d746f703a31707820736f6c696420236530653065303b6865696768743a333970783b6f766572666c6f773a68696464656e7d2e4e365257567b6865696768743a353170783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e5576363771627b626f782d7061636b3a6a7573746966793b666f6e742d73697a653a313270783b6c696e652d6865696768743a333770783b6a7573746966792d636f6e74656e743a73706163652d6265747765656e3b6a7573746966792d636f6e74656e743a73706163652d6265747765656e7d2e55763637716220612c2e557636377162207370616e7b636f6c6f723a233735373537353b646973706c61793a626c6f636b3b666c65783a6e6f6e653b70616464696e673a3020313670783b746578742d616c69676e3a63656e7465723b746578742d7472616e73666f726d3a7570706572636173653b7d7370616e2e4f585875707b626f726465722d626f74746f6d3a32707820736f6c696420233432383566343b636f6c6f723a233432383566343b666f6e742d7765696768743a626f6c647d612e655a743878643a766973697465647b636f6c6f723a233735373537357d2e46456c6273667b626f726465722d6c6566743a31707820736f6c6964207267626128302c302c302c2e3132297d6865616465722061727469636c657b6f766572666c6f773a76697369626c657d2e5067373062667b6865696768743a333970783b646973706c61793a626f783b646973706c61793a666c65783b646973706c61793a666c65783b77696474683a313030257d2e4830505165637b706f736974696f6e3a72656c61746976653b666c65783a317d2e7362637b646973706c61793a666c65783b77696474683a313030257d2e50673730626620696e7075747b6d617267696e3a3270782034707820327078203870783b7d2e787b77696474683a323670783b636f6c6f723a233735373537353b666f6e743a323770782f3338707820617269616c2c2073616e732d73657269663b6c696e652d6865696768743a343070783b7d237164436c77627b666c65783a302030206175746f3b77696474683a333970783b6865696768743a333970783b626f726465722d626f74746f6d3a303b70616464696e673a303b626f726465722d746f702d72696768742d7261646975733a3870783b6261636b67726f756e642d636f6c6f723a233362373865373b626f726465723a31707820736f6c696420233333363764363b6261636b67726f756e642d696d6167653a75726c28646174613a696d6167652f6769663b6261736536342c52306c474f4464684a41416a41504948414f44722f6e436b2b4d505a2f466d5639367a4b2b2f372b2f354b352b6b714c39697741414141414a41416a41454144616e693633503477796b6d624b635152584473635141454d586d6d65614c51564c43756b7a79433039416a66654b37762f4d41616a41434c68504d564167776a73556345695a61387867415972567176324b783269777349414141426b6e664242414b7254453449634d796f743875723864617471496251664a646e41666f3257453642563035775849694a69676b414f773d3d293b7d2e73637b666f6e742d73697a653a3b706f736974696f6e3a6162736f6c7574653b746f703a333970783b6c6566743a303b72696768743a303b626f782d736861646f773a3070782032707820357078207267626128302c302c302c302e32293b7a2d696e6465783a323b6261636b67726f756e642d636f6c6f723a236666667d2e73633e6469767b70616464696e673a3130707820313070783b70616464696e672d6c6566743a313670783b70616464696e672d6c6566743a313470783b626f726465722d746f703a31707820736f6c696420234446453145357d2e7363737b6261636b67726f756e642d636f6c6f723a236635663566353b7d2e6e6f484978637b646973706c61793a626c6f636b3b666f6e742d73697a653a313670783b70616464696e673a3020302030203870783b666c65783a313b6865696768743a333570783b6f75746c696e653a6e6f6e653b626f726465723a6e6f6e653b77696474683a313030253b2d7765626b69742d7461702d686967686c696768742d636f6c6f723a7267626128302c302c302c30293b6f766572666c6f773a68696464656e3b7d2e73626320696e7075745b747970653d746578745d7b6261636b67726f756e643a6e6f6e657d2e736d6c202e634f6c3449647b646973706c61793a6e6f6e657d2e6c7b646973706c61793a6e6f6e657d2e736d6c206865616465727b6261636b67726f756e643a6e6f6e657d2e736d6c202e6c7b646973706c61793a626c6f636b3b70616464696e673a30203870787d2e736d6c202e6c7b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b626f726465722d7261646975733a3270782030203020303b666f6e743a323270782f33367078204675747572612c20417269616c2c2073616e732d73657269663b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e627a316c42627b6261636b67726f756e643a236666663b626f726465722d7261646975733a38707820387078203020303b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d746f703a313070787d2e4b50374c43627b626f726465722d7261646975733a30203020387078203870783b626f782d736861646f773a30203270782033707820726762612833322c2033332c2033362c20302e3138293b6d617267696e2d626f74746f6d3a313070783b6f766572666c6f773a68696464656e7d2e634f6c3449647b6c65747465722d73706163696e673a2d3170783b746578742d616c69676e3a63656e7465723b666f6e743a32327074204675747572612c20417269616c2c2073616e732d73657269663b70616464696e673a3130707820302035707820303b6865696768743a333770783b666f6e742d736d6f6f7468696e673a616e7469616c69617365647d2e634f6c344964207370616e7b646973706c61793a696e6c696e652d626c6f636b7d2e533539316a7b6865696768743a313030257d2e5636677756647b636f6c6f723a233432383546347d2e69576b7576647b636f6c6f723a234541343333357d2e63447251377b636f6c6f723a236662636330357d2e6e746c52397b636f6c6f723a233334413835337d2e744a334d79637b2d7765626b69742d7472616e73666f726d3a726f74617465282d3230646567293b706f736974696f6e3a72656c61746976653b6c6566743a2d3170783b646973706c61793a696e6c696e652d626c6f636b7d666f6f7465727b746578742d616c69676e3a63656e7465723b6d617267696e2d746f703a313870787d666f6f74657220612c666f6f74657220613a766973697465642c2e736d695562627b636f6c6f723a233566363336387d2e6b73545534637b6d617267696e3a3020313370787d236d436c6a6f627b6d617267696e2d746f703a333670787d236d436c6a6f623e6469767b6d617267696e3a323070787d3c2f7374796c653e3c2f686561643e3c626f6479206a736d6f64656c3d2220223e3c6865616465722069643d22686472223e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220633d3530303b2866756e6374696f6e28297b77696e646f772e73637265656e262677696e646f772e73637265656e2e77696474683c3d63262677696e646f772e73637265656e2e6865696768743c3d632626646f63756d656e742e676574456c656d656e7442794964282268647222292e636c6173734c6973742e6164642822736d6c22293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c64697620636c6173733d22634f6c344964223e3c6120687265663d222f3f73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673514f776743223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c2f6469763e3c64697620636c6173733d22627a316c4262223e3c666f726d20636c6173733d22506737306266222069643d227366223e3c6120636c6173733d226c2220687265663d222f3f6f75747075743d73656172636826616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735150416745223e3c7370616e20636c6173733d22563667775664223e473c2f7370616e3e3c7370616e20636c6173733d2269576b757664223e6f3c2f7370616e3e3c7370616e20636c6173733d226344725137223e6f3c2f7370616e3e3c7370616e20636c6173733d22563667775664223e673c2f7370616e3e3c7370616e20636c6173733d226e746c5239223e6c3c2f7370616e3e3c7370616e20636c6173733d2269576b75766420744a334d7963223e653c2f7370616e3e3c2f613e3c696e707574206e616d653d226965222076616c75653d2249534f2d383835392d312220747970653d2268696464656e223e3c64697620636c6173733d22483050516563223e3c64697620636c6173733d227362632065736263223e3c696e70757420636c6173733d226e6f48497863222076616c75653d226178696f7322206175746f6361706974616c697a653d226e6f6e6522206175746f636f6d706c6574653d226f666622206e616d653d227122207370656c6c636865636b3d2266616c73652220747970653d2274657874223e3c696e707574206e616d653d226f712220747970653d2268696464656e223e3c696e707574206e616d653d226171732220747970653d2268696464656e223e3c64697620636c6173733d2278223ed73c2f6469763e3c64697620636c6173733d227363223e3c2f6469763e3c2f6469763e3c2f6469763e3c627574746f6e2069643d227164436c77622220747970653d227375626d6974223e3c2f627574746f6e3e3c2f666f726d3e3c2f6469763e3c6e6f7363726970743e3c6d65746120636f6e74656e743d22303b75726c3d2f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b505942512220687474702d65717569763d2272656672657368223e3c7374796c653e7461626c652c6469762c7370616e2c707b646973706c61793a6e6f6e657d3c2f7374796c653e3c646976207374796c653d22646973706c61793a626c6f636b223e506c6561736520636c69636b203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b6762763d3126616d703b7365693d704d524f586250624472505339414f426d4b50594251223e686572653c2f613e20696620796f7520617265206e6f7420726564697265637465642077697468696e206120666577207365636f6e64732e3c2f6469763e3c2f6e6f7363726970743e3c2f6865616465723e3c6469762069643d226d61696e223e3c6469763e3c64697620636c6173733d224b50374c4362223e203c64697620636c6173733d22625273576e63223e203c64697620636c6173733d224e36525756223e203c64697620636c6173733d2250673730626620557636377162223e203c7370616e20636c6173733d224f58587570223e416c6c3c2f7370616e3e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6e777326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943436742223e4e6577733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d76696426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943536743223e566964656f733c2f613e3c6120636c6173733d22655a743878642220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d6973636826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943696744223e496d616765733c2f613e2020203c6120687265663d22687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d6178696f7326616d703b756d3d3126616d703b69653d5554462d3826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554943796745223e4d6170733c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d73686f7026616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944436746223e53686f7070696e673c2f613e20203c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e6d7326616d703b74626d3d626b7326616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673515f41554944536747223e426f6f6b733c2f613e20202020203c64697620636c6173733d2246456c627366223e3c6120687265663d222f616476616e6365645f73656172636822207374796c653d2277686974652d73706163653a6e6f77726170222069643d2273742d746f67676c652220726f6c653d22627574746f6e223e53656172636820746f6f6c733c2f613e3c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e203c2f6469763e3c64697620636c6173733d22506737306266207745736a6264205a494e62626320787064204f396735636320755550476922207374796c653d22646973706c61793a6e6f6e65222069643d2273742d63617264223e3c7374796c653e2e7745736a62647b6261636b67726f756e642d636f6c6f723a236666663b6865696768743a343470783b77686974652d73706163653a6e6f777261707d2e636f505538637b6865696768743a363070783b6f766572666c6f772d7363726f6c6c696e673a746f7563683b6f766572666c6f772d783a6175746f3b6f766572666c6f772d793a68696464656e7d2e586a326175657b6865696768743a343470783b6f766572666c6f773a68696464656e7d2e526e4e477a657b6d617267696e3a3131707820313670787d2e7745736a6264206469762c2e7745736a626420612c2e7745736a6264206c697b6f75746c696e652d77696474683a303b6f75746c696e653a6e6f6e657d3c2f7374796c653e3c64697620636c6173733d22586a32617565223e3c64697620636c6173733d22636f50553863223e3c64697620636c6173733d22526e4e477a65223e3c7374796c653e2e5041394a357b646973706c61793a696e6c696e652d626c6f636b7d2e5258614f66647b646973706c61793a696e6c696e652d626c6f636b3b6865696768743a323270783b706f736974696f6e3a72656c61746976653b70616464696e672d746f703a303b70616464696e672d626f74746f6d3a303b70616464696e672d72696768743a313670783b70616464696e672d6c6566743a303b6c696e652d6865696768743a323270783b637572736f723a706f696e7465723b746578742d7472616e73666f726d3a7570706572636173653b666f6e742d73697a653a313270783b636f6c6f723a233735373537357d2e736131746f637b646973706c61793a6e6f6e653b706f736974696f6e3a6162736f6c7574653b6261636b67726f756e643a236666663b626f726465723a31707820736f6c696420236436643664363b626f782d736861646f773a302032707820347078207267626128302c302c302c302e33293b6d617267696e3a303b77686974652d73706163653a6e6f777261703b7a2d696e6465783a3130333b6c696e652d6865696768743a313770783b70616464696e672d746f703a3570783b70616464696e672d626f74746f6d3a3570783b70616464696e672d6c6566743a3070787d2e5041394a353a686f766572202e736131746f637b646973706c61793a626c6f636b7d2e6d475379386420613a6163746976652c2e5258614f66643a6163746976657b636f6c6f723a233432383566347d3c2f7374796c653e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c7374796c653e2e54574d4f55637b646973706c61793a696e6c696e652d626c6f636b3b70616464696e672d72696768743a313470783b77686974652d73706163653a6e6f777261707d2e7651597547667b666f6e742d7765696768743a626f6c647d2e4f6d54497a667b626f726465722d636f6c6f723a23393039303930207472616e73706172656e743b626f726465722d7374796c653a736f6c69643b626f726465722d77696474683a347078203470782030203470783b77696474683a303b6865696768743a303b6d617267696e2d6c6566743a2d313070783b746f703a3530253b6d617267696e2d746f703a2d3270783b706f736974696f6e3a6162736f6c7574657d2e5258614f66643a616374697665202e4f6d54497a667b626f726465722d636f6c6f723a23343238356634207472616e73706172656e747d3c2f7374796c653e3c64697620636c6173733d2254574d4f5563223e416e792074696d653c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c7374796c653e2e6f7a61744d7b666f6e742d73697a653a313270783b746578742d7472616e73666f726d3a7570706572636173657d2e6f7a61744d202e794e46736c2c2e6f7a61744d206c697b6c6973742d7374796c652d747970653a6e6f6e653b6c6973742d7374796c652d706f736974696f6e3a6f7574736964653b6c6973742d7374796c652d696d6167653a6e6f6e657d2e794e46736c2e536b556a34632c2e794e46736c20617b636f6c6f723a7267626128302c302c302c302e3534293b746578742d6465636f726174696f6e3a6e6f6e653b70616464696e673a36707820343470782036707820313470783b6c696e652d6865696768743a313770783b646973706c61793a626c6f636b7d2e536b556a34637b6261636b67726f756e642d696d6167653a75726c282f2f73736c2e677374617469632e636f6d2f75692f76312f6d656e752f636865636b6d61726b322e706e67293b6261636b67726f756e642d706f736974696f6e3a72696768742063656e7465723b6261636b67726f756e642d7265706561743a6e6f2d7265706561747d2e536b556a34633a6163746976657b6261636b67726f756e642d636f6c6f723a236635663566357d3c2f7374796c653e3c6c6920636c6173733d22794e46736c20536b556a3463223e416e792074696d653c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6826616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494477223e5061737420686f75723c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6426616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494541223e5061737420323420686f7572733c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7726616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494551223e50617374207765656b3c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a6d26616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494567223e50617374206d6f6e74683c2f613e3c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d7164723a7926616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494577223e5061737420796561723c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c64697620636c6173733d225041394a35223e3c64697620636c6173733d225258614f66642220726f6c653d22627574746f6e2220746162696e6465783d2230223e3c64697620636c6173733d2254574d4f5563223e416c6c20726573756c74733c2f6469763e3c7370616e20636c6173733d224f6d54497a66223e3c2f7370616e3e3c2f6469763e3c756c20636c6173733d22736131746f63206f7a61744d223e3c6c6920636c6173733d22794e46736c20536b556a3463223e416c6c20726573756c74733c2f6c693e3c6c6920636c6173733d22794e46736c223e3c6120687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b736f757263653d6c6e7426616d703b7462733d6c693a3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351707755494651223e566572626174696d3c2f613e3c2f6c693e3c2f756c3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220613d646f63756d656e742e676574456c656d656e7442794964282273742d746f67676c6522292c623d646f63756d656e742e676574456c656d656e7442794964282273742d6361726422293b612626622626612e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2863297b622e7374796c652e646973706c61793d622e7374796c652e646973706c61793f22223a226e6f6e65223b632e70726576656e7444656661756c7428297d2c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c2f6469763e3c7374796c653e2e5a494e6262637b6261636b67726f756e642d636f6c6f723a236666663b6d617267696e2d626f74746f6d3a313070783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b626f726465722d7261646975733a3870787d2e75555047697b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070783b7d2e4f39673563633e2a3a66697273742d6368696c647b626f726465722d746f702d6c6566742d7261646975733a3870783b626f726465722d746f702d72696768742d7261646975733a3870787d2e4f39673563633e2a3a6c6173742d6368696c647b626f726465722d626f74746f6d2d6c6566742d7261646975733a3870783b626f726465722d626f74746f6d2d72696768742d7261646975733a3870787d2e726c37696c627b646973706c61793a626c6f636b3b636c6561723a626f74687d2e6c634a4631647b6d617267696e2d6c6566743a313670783b666c6f61743a72696768743b7d2e6b437259547b70616464696e673a31327078203136707820313270787d612e6664597371667b636f6c6f723a233442313141387d2e424e656177657b77686974652d73706163653a7072652d6c696e653b776f72642d777261703a627265616b2d776f72647d2e76766a774a627b636f6c6f723a233139363744323b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070787d2e76766a774a6220613a766973697465647b636f6c6f723a233442313141387d2e76766a774a622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e76766a774a622e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69747b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e55506d69742e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e55506d69742e415037576e647b636f6c6f723a7267626128302c3130322c33332c31297d2e7835346774667b6865696768743a3170783b6261636b67726f756e642d636f6c6f723a236466653165353b6d617267696e3a3020313670787d2e4170354f53647b70616464696e672d626f74746f6d3a313270787d2e7333763972647b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e7333763972642e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7333763972642e415037576e647b636f6c6f723a233230323132347d2e6d53783145657b70616464696e672d6c6566743a343870783b6d617267696e3a307d2e7639693631657b70616464696e672d626f74746f6d3a3870787d2e584c6c6f58657b636f6c6f723a233139363744323b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e584c6c6f586520613a766973697465647b636f6c6f723a233442313141387d2e584c6c6f58652e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e584c6c6f58652e48724764656220613a766973697465647b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e5a54763942627b646973706c61793a626c6f636b7d2e6465497643627b666f6e742d73697a653a313670783b6c696e652d6865696768743a323070783b666f6e742d7765696768743a3430307d2e6465497643622e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e6465497643622e415037576e647b636f6c6f723a233230323132347d2e4643557030637b666f6e742d7765696768743a626f6c647d2e58374e5456657b646973706c61793a7461626c653b77696474683a313030253b70616464696e672d72696768743a313670783b626f782d73697a696e673a626f726465722d626f787d2e74486d6651657b646973706c61793a7461626c652d63656c6c3b70616464696e673a313270782030203132707820313670787d2e554874726b7b77696474683a373270787d2e4842544d36647b77696474683a333070787d2e5853377947647b646973706c61793a7461626c652d63656c6c3b746578742d616c69676e3a63656e7465723b766572746963616c2d616c69676e3a6d6964646c653b70616464696e673a3132707820302031327078203870787d2e616d335142667b646973706c61793a7461626c653b766572746963616c2d616c69676e3a746f707d2e5862355652657b636f6c6f723a233139363744327d613a76697369746564202e5862355652657b636f6c6f723a233442313141387d2e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c31297d613a76697369746564202e5862355652652e74723064777b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438447b666f6e742d73697a653a313470783b6c696e652d6865696768743a323070787d2e74416438442e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e74416438442e415037576e647b636f6c6f723a233730373537417d2e614a79694f637b636f6c6f723a233030363632317d2e6f754a3943627b666c6f61743a6c6566743b70616464696e672d72696768743a313670787d2e526f434c6e657b636c6561723a626f74687d2e45594f736c647b646973706c61793a696e6c696e652d626c6f636b3b706f736974696f6e3a72656c61746976657d2e424669395a627b6f766572666c6f773a68696464656e3b706f736974696f6e3a72656c61746976657d2e53374a647a657b616c69676e2d6974656d733a63656e7465723b646973706c61793a666c65783b666c65782d646972656374696f6e3a636f6c756d6e3b6a7573746966792d636f6e74656e743a73706163652d61726f756e647d2e58646c7230647b6f766572666c6f772d783a6175746f3b2d7765626b69742d6f766572666c6f772d7363726f6c6c696e673a746f7563683b6d617267696e3a30202d3870783b70616464696e673a313670782030203136707820323470783b70616464696e672d746f703a3270783b6d617267696e2d746f703a2d3270783b7472616e73666f726d3a7472616e736c617465336428302c302c30297d2e6964673862657b646973706c61793a7461626c653b626f726465722d636f6c6c617073653a73657061726174653b626f726465722d73706163696e673a38707820303b6d617267696e3a30202d3870783b70616464696e672d72696768743a313670787d2e425647304e627b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a746f703b6261636b67726f756e642d636f6c6f723a236666663b626f726465722d7261646975733a3870783b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238293b6f766572666c6f773a68696464656e7d2e55796b5439647b646973706c61793a626c6f636b3b666c6f61743a72696768743b70616464696e672d6c6566743a313670787d2e6e59543751627b636c6561723a626f74687d2e736b566770627b646973706c61793a7461626c653b7461626c652d6c61796f75743a66697865643b77696474683a313030257d2e5647484d58647b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c653b6865696768743a353270783b746578742d616c69676e3a63656e7465727d2e4c70614472627b6d617267696e3a30206175746f203870783b646973706c61793a626c6f636b7d2e766253684f657b70616464696e672d746f703a307d2e4156736570667b70616464696e672d626f74746f6d3a3870787d2e4156736570662e753278314f647b70616464696e672d626f74746f6d3a307d2e787063202e6877632c2e787078202e6877787b646973706c61793a6e6f6e657d2e5275386964627b6d617267696e2d746f703a2d313670787d2e70756e657a7b666f6e742d7765696768743a3730303b6c65747465722d73706163696e673a302e373570783b746578742d7472616e73666f726d3a7570706572636173657d2e7779727758637b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e7779727758632e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c31297d2e7779727758632e415037576e647b636f6c6f723a233230323132347d2e6d4868796c667b646973706c61793a7461626c652d63656c6c3b766572746963616c2d616c69676e3a6d6964646c657d2e575a35474a667b616c69676e2d6974656d733a63656e7465723b70616464696e673a3020323070783b6d696e2d77696474683a31313270787d2e714e394b65642c2e44586b354d657b6d617267696e3a30206175746f7d2e44586b354d657b6d617267696e2d626f74746f6d3a313270787d2e51693946647b6261636b67726f756e643a236666663b626f726465723a303b626f726465722d7261646975733a39393970783b646973706c61793a626c6f636b3b6865696768743a353670783b6a7573746966792d636f6e74656e743a63656e7465723b77696474683a353670783b7a2d696e6465783a307d2e51693946647b626f782d736861646f773a30203170782036707820726762612833322c2033332c2033362c20302e3238292c696e7365742030203020302030207267626128302c302c302c302e3130292c696e73657420302030203020302072676261283235352c3235352c3235352c302e3530297d2e51693946643a666f6375737b6f75746c696e653a6e6f6e657d2e5169394664202e685748754a7b646973706c61793a626c6f636b3b6d617267696e3a30206175746f7d2e6a69356a70667b746578742d616c69676e3a63656e7465727d68727b626f726465723a303b626f726465722d626f74746f6d3a31707820736f6c696420236466653165353b6d617267696e3a303b7d2e425579624b652c2e48736e4642667b6d617267696e2d6c6566743a313670787d2e425579624b652c2e6f4d3247417b6d617267696e2d72696768743a313670787d2e584f377268637b6d617267696e3a30202d313670787d2e6949576d34627b626f782d73697a696e673a626f726465722d626f783b6d696e2d6865696768743a343870787d2e664c745873637b70616464696e673a313470783b746578742d616c69676e3a63656e7465727d2e4c796d38577b77696474683a313470783b6865696768743a323070783b706f736974696f6e3a72656c61746976653b6d617267696e3a30206175746f7d2e4164665872627b6d617267696e2d6c6566743a2d313470783b766572746963616c2d616c69676e3a6d6964646c653b646973706c61793a696e6c696e652d626c6f636b7d2e4c796d3857206469767b706f736974696f6e3a6162736f6c7574653b626f726465722d6c6566743a37707820736f6c6964207472616e73706172656e743b626f726465722d72696768743a37707820736f6c6964207472616e73706172656e743b77696474683a303b6865696768743a303b6c6566743a307d2e4979596145647b746f703a3770783b626f726465722d746f703a37707820736f6c696420233735373537357d2e4543554851657b746f703a3470783b626f726465722d746f703a37707820736f6c696420236666667d2e4165515175627b626f74746f6d3a3770783b626f726465722d626f74746f6d3a37707820736f6c696420233735373537357d2e5943553765627b626f74746f6d3a3470783b626f726465722d626f74746f6d3a37707820736f6c696420236666667d2e4963783643647b6d617267696e3a30206175746f203870787d2e6d41646a51637b746578742d616c69676e3a72696768747d2e75456563337b666f6e742d73697a653a313270783b6c696e652d6865696768743a313670787d2e75456563332e4872476465627b636f6c6f723a72676261283235352c3235352c3235352c2e37297d2e75456563332e415037576e647b636f6c6f723a233730373537417d2e724c736879662c2e426d503574667b70616464696e672d746f703a313270783b70616464696e672d626f74746f6d3a313270787d2e773143334c652c2e426d503574662c2e47354e6242647b70616464696e672d6c6566743a313670783b70616464696e672d72696768743a313670783b7d2e47354e6242647b70616464696e672d626f74746f6d3a313270787d2e6e4d796d65667b646973706c61793a666c65787d2e473565466c667b666c65783a313b646973706c61793a626c6f636b7d2e6e4d796d6566207370616e7b746578742d616c69676e3a63656e7465727d3c2f7374796c653e3c6469763e3c212d2d53575f435f582d2d3e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4141656751494278414226616d703b7573673d414f76566177327466496430584346385649396a682d324f35555069223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f733c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e6178696f732e636f6d3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d224170354f5364223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536d6172742c20656666696369656e74206e65777320776f72746879206f6620796f75722074696d652c20617474656e74696f6e2c20616e642074727573742e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f706f6c697469637326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741586f4543416351417726616d703b7573673d414f765661773139685044464b57637a653030627974527851476576223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e506f6c69746963733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f617574686f72732f6e6577736465736b26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177416e6f4543416351425126616d703b7573673d414f7656617730315a6b4f30626d50386e4370657152386257474959223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e53746f72696573206279204178696f733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6178696f732d64617368626f6172642d313535303631393334332d64653734306436612d656163312d343539392d383262642d3434303533343537376639322e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417741336f4543416351427726616d703b7573673d414f76566177305658706145566f434f444153557657784f61466652223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4178696f732044617368626f6172643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c65747465727326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742486f4543416351435126616d703b7573673d414f76566177316255574b54564d7a4944436d736a34536d614c4755223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e4e6577736c6574746572733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d22763969363165223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f776f726c6426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a42417742586f4543416351437726616d703b7573673d414f765661773246776f732d5f6e416d39666e4963486a754b325a53223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e576f726c643c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f627573696e65737326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516a424177426e6f4543416351445126616d703b7573673d414f76566177324366426a4a724a72716e5052623241754d614e4953223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e427573696e6573733c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e546f702073746f726965733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f6e6577736c6574746572732f6178696f732d616d2d61653065626362352d616432352d346531632d623662642d3138633530393464656330312e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4164364241674145414926616d703b7573673d414f765661773148445a7242345437756b327137384256725676426f223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e4178696f7320414d202d2041756775737420392c20323031393c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f636f756e74726965732d6d6f73742d7269736b2d77617465722d6372697369732d66343066343866392d623032652d346536622d393965642d3136346335636239353333352e68746d6c26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351714f63424d4168364241674145415526616d703b7573673d414f7656617733505243566f5a5532675a494b44336b62615072544e223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d2272514d516f6420586235565265223e54686520636f756e7472696573206d6f7374206174207269736b206f662061207761746572206372697369733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e3c7370616e20636c6173733d2272514d516f6420614a79694f63223e4178696f733c2f7370616e3e20b72031206461792061676f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545617574686f7226616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673513646347743586f4543415951416726616d703b7573673d414f7656617733626475774e6f5934356137397a30464775566e566d223e3c64697620636c6173733d226b43725954223e3c64697620636c6173733d226f754a3943622053374a647a6522207374796c653d2277696474683a343070783b6865696768743a34307078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a343070783b6d61782d6865696768743a34307078222069643d2264696d675f312220646174612d64656665727265643d2231223e3c2f6469763e3c6469763e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320262331303030333b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e54776974746572202623383235303b206178696f733c2f6469763e3c2f6469763e3c64697620636c6173733d22526f434c6e65223e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838373636383733393733393634382533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177436e6f4543415951424126616d703b7573673d414f7656617733765932387a4a6374626a64305a365357505a344c63223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5477697474657220686173207265696e737461746564204d69746368204d63436f6e6e656c6c27732072652d656c656374696f6e2063616d706169676e206163636f756e74206166746572207468652070726f66696c65207761732073757370656e646564206561726c6965722074686973207765656b2e207777772e6178696f732e636f6d2f6d697463682d6d63636f6e2623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e313920686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393838333433333933313336363430322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517743336f4543415951426726616d703b7573673d414f76566177336c4b50674566354d5351724b75784655584d614f41223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e657720746563686e6f6c6f677920697320726573686170696e67206173736574206d616e6167656d656e7420666f722061206e65772067656e65726174696f6e206f6620776f726b6572732c20616e64206d6f7374206f662074686520696e64757374727920686173206e6f74206b65707420706163652e207777772e6178696f732e636f6d2f61737365742d6d616e61672623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837373735323139323939353333332533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744486f4543415951434126616d703b7573673d414f7656617732554b4158715044627577556a736d37626342675874223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5374657068656e20526f73732022667265616b6564206f75742220617420746865206261636b6c61736820746f20686973205472756d702066756e64726169736572206173205472756d70206173736f636961746573207065727375616465642068696d20746f20676f206168656164207769746820746865206576656e742061742068697320536f757468616d70746f6e206d616e73696f6e2e207777772e6178696f732e636f6d2f7374657068656e2d726f732623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393837323436373432383530373634392533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c517744586f4543415951436726616d703b7573673d414f7656617733683854556374412d32397252656e584742497a7a5f223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4265796f6e6420726570616972696e6720616e6420696d70726f76696e6720726f61647320616e64207369646577616c6b732c20636974696573206861766520616e206f70706f7274756e69747920746f206275696c6420696e667261737472756374757265207468617420636f756c64206f70656e207570206e6577206d6f62696c697479206f7074696f6e7320616e6420696e637265617365206163636573736962696c6974792c207772697465732045787065727420566f6963657320636f6e7472696275746f722048656e727920436c6179706f6f6c2e207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323020686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f75726c3f713d68747470733a2f2f747769747465722e636f6d2f6178696f732f7374617475732f313135393836373138323634333430343830322533467265665f73726325334474777372632532353545676f6f676c652532353743747763616d702532353545736572702532353743747767722532353545747765657426616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351676c5177446e6f4543415951444126616d703b7573673d414f765661773056326641356f4573505f6b723267467962676b5568223e3c6469763e3c646976207374796c653d2277696474683a3233327078223e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e414d442043454f204c697361205375206469736375737365732077697468204178696f732068657220636f6d70616e79277320726573757267656e636520616e6420626174746c6573207769746820496e74656c207777772e6178696f732e636f6d2f616d642d6c6973612d73752623383233303b3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e323120686f7572732061676f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f6769746875622e636f6d2f6178696f732f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4150656751494242414226616d703b7573673d414f7656617733486a474b6775516d6342745456445566344d665072223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e6178696f732f6178696f733a2050726f6d697365206261736564204854545020636c69656e7420666f72207468652062726f7773657220616e64202e2e2e202d204769744875623c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f6769746875622e636f6d202623383235303b206178696f73202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e636f6e7374206178696f73203d207265717569726528276178696f7327293b202f2f204d616b652061207265717565737420666f72206120757365722077697468206120676976656e204944206178696f732e2067657428272f757365723f49443d31323334352729202e7468656e2866756e6374696f6e2028726573706f6e736529207b202f2f2068616e646c652073756363657373a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e6178696f732e636f6d2f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517757347745486f4543416b51416726616d703b7573673d414f7656617733565033564769536e46355355474c45653730387037223e3c696d6720636c6173733d2255796b5439642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a373270783b6d61782d6865696768743a37327078222069643d2264696d675f32332220646174612d64656665727265643d2231223e3c2f613e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e4178696f733c2f6469763e3c2f7370616e3e3c7370616e3e3c64697620636c6173733d22424e6561776520744164384420415037576e64223e576562736974653c2f6469763e3c2f7370616e3e3c64697620636c6173733d226e5954375162223e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e204974206f6666696369616c6c79206c61756e6368656420696e20323031372e2054686520736974652773206e616d65206973206261736564206f6e2074686520477265656b3a202623373934303b26233935383b26233935333b26233935393b26233936323b2c206d65616e696e672022776f72746879222e203c7370616e20636c6173733d22424e65617765223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673516d684d77456e6f4543416b51427726616d703b7573673d414f76566177306a76706f32364a65425a684a7638496157384e7163223e3c7370616e20636c6173733d22584c6c6f586520415037576e64223e57696b6970656469613c2f7370616e3e3c2f613e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22766253684f65206b43725954223e3c64697620636c6173733d22415673657066223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4f776e65723c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e4178696f73204d6564696120496e633c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c64697620636c6173733d2241567365706620753278314f64223e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c7370616e3e3c7370616e20636c6173733d22424e656177652073337639726420415037576e64223e4c61756e636865643c2f7370616e3e3c2f7370616e3e3a203c7370616e3e3c7370616e20636c6173733d22424e6561776520744164384420415037576e64223e323031373c2f7370616e3e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e50656f706c6520616c736f2073656172636820666f723c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22787063223e3c64697620636c6173733d22783534677466223e3c2f6469763e3c6469763e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c206e65777320736f7572636573206c6973743c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d434e4e26616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335343394f4c7a4b537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d7851685866497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a333844437549695632646e504477435833376e476b414141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454149223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e434e4e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d506f6c697469636f26616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d444b504e7971334e4a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45556b6e6c4a4f52573153516e6752566c4778516e414d566a795f4d4d7339495f38556f3659504c5f415957786b5773484148354f5a6b6c6d636e3541484749687547584141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945414d223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e506f6c697469636f3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d48756666506f737426616d703b737469636b3d483473494141414141414141414f4f5155654c537a395533794d67744b6b68504d704c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794146536e6c3663586e534b45617a5479447a65714e7753796f45594131575562464363417857504c3877797a306a5f78536a7067387638426862475261776348715670615148357853554175376a463070634141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454151223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e48756666506f73743c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4e505226616d703b737469636b3d483473494141414141414141414f4f5155654c517a3955335344596f7a6a47537a4d6c4d5369314b7a4648495379307656696a4f4c79314b54693157794d6b734c6f6c6954617a497a43382d785168576e463663586e534b6b5176454e444b504e79713368484979636f734b30704f67696b416d5173586a4337504d4d394a5f4d55723634444b5f675956784553757a583041514142514b584c32514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454155223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4e6174696f6e616c205075626c69632052612e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b6e6577732b736f75726365732b6c69737426616d703b737469636b3d483473494141414141414141414f4f514d5a4c4d7955784b4c55724d5563684c4c5339574b4d34764c55704f4c5662497953777569574a4e724d6a4d4c7a3746794b476671322d515870786564497152433851304d6f38334b726545636a4a79697772536b36434b6b67324b633644693859565a35686e707678676c6658435a3338444375496756747a514146695a305070774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454159223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f31312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c64697620636c6173733d22687763223e3c64697620636c6173733d226b43725954223e3c7370616e20636c6173733d2270756e657a223e3c64697620636c6173733d22424e656177652077797277586320415037576e64223e4c69626572616c20706f6c69746963616c2077656273697465733c2f6469763e3c2f7370616e3e3c2f6469763e3c6469763e3c6469763e3c64697620636c6173733d2258646c723064223e3c64697620636c6173733d22696467386265223e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d536c6174652b4d6167617a696e6526616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d4449724e7259776b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a5762787875565730493547626c4642656c4a70786752426b456c7a4d334b636f70796f524c47795a55705656433253564a4b5354715562576c55626c7a796931484b423666564453794d69316a35676e4d5353314956664250544536737938314942452d4b766d72674141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454167223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31332220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e536c617465204d6167617a696e653c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d42757a7a4665656426616d703b737469636b3d483473494141414141414141414f4f5155654c537a3955334d44637279796e4b4e5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a69303878677455626d6363626c5674434f526d3552515870536163594f6345795a735847466c414a694b6c514365506b797051714b4e736b4b61556b486371324e436f334c766e464b4f5744302d6f4746735a467242784f7056565662716d704b5144564e44327173774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416b223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31352220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e42757a7a466565643c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d4461696c792b4b6f7326616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d453675544b6b796b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524a67473642736b3653556b6e516f32394b6f334c6a6b46364f554430367247316759463746797569526d356c5171654f635841774447505a396673774141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a364241674945416f223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31372220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4461696c79204b6f733c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c6120636c6173733d22425647304e622220687265663d222f7365617263683f69653d5554462d3826616d703b713d54616c6b696e672b506f696e74732b4d656d6f26616d703b737469636b3d483473494141414141414141414f4f5155654c557a3955334d456c4b4b556b336b73724a54456f74537378524b4d6a5079537a4a54416179796c4f54696a4e4c556f756a57424d724d764f4c547a4679675a51626d6363626c5674434f526d3552515870536163597751595a6d5255625730416c7a4d334b636f70796f524c47795a5570565641323244596f32394b6f334c6a6b46364f5544303672473167594637454b6879546d5a47666d705373453547666d6c5251722d4b626d35674d415637446f6d62304141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735173396f424d425a3642416749454173223e3c6469763e3c646976207374796c653d2277696474683a3131327078223e3c64697620636c6173733d2253374a647a6522207374796c653d2277696474683a31313270783b6865696768743a3131327078223e3c696d6720636c6173733d2245594f736c642220616c743d2222207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a31313270783b6d61782d6865696768743a3131327078222069643d2264696d675f31392220646174612d64656665727265643d2231223e3c2f6469763e3c64697620636c6173733d22525775676763206b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e54616c6b696e6720506f696e7473204d652e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d226d4868796c66223e3c64697620636c6173733d22575a35474a66223e3c6120636c6173733d22714e394b65642220687265663d222f7365617263683f69653d5554462d3826616d703b713d4c69626572616c2b706f6c69746963616c2b776562736974657326616d703b737469636b3d483473494141414141414141414f4f514d5a4c4b7955784b4c55724d55536a497a386b7379557747737370546b346f7a53314b4c6f3167544b7a4c7a6930387863756e6e366873596d6363626c5674434f526d3552515870536163594f6345795a735847466c414a63374f796e4b4a6371495278636d564b465a52746b7052536b67356c57787156473566385970547977576c314177766a496c5938386744456f63564e75514141414126616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517a4f30424d425a3642416749454177223e3c627574746f6e20636c6173733d2244586b354d65205169394664223e3c696d6720636c6173733d22685748754a2220616c743d224172726f7722207372633d22646174613a696d6167652f6769663b6261736536342c52306c474f446c68415141424149414141502f2f2f2f2f2f2f7948354241454b414145414c414141414141424141454141414943544145414f773d3d22207374796c653d226d61782d77696474683a323470783b6d61782d6865696768743a32347078222069643d2264696d675f32312220646174612d64656665727265643d2231223e3c2f627574746f6e3e3c64697620636c6173733d22424e65617765206a69356a706620744164384420415037576e64223e4d6f726520726573756c74733c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c68723e3c64697620636c6173733d226475662d68223e3c64697620636c6173733d22664c74587363206949576d346222206f6e636c69636b3d227870287468697329223e3c64697620636c6173733d224c796d3857223e3c64697620636c6173733d2241655151756220687763223e3c2f6469763e3c64697620636c6173733d2259435537656220687763223e3c2f6469763e3c64697620636c6173733d2249795961456420687778223e3c2f6469763e3c64697620636c6173733d2245435548516520687778223e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f4178696f735f28776562736974652926616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4159656751494252414226616d703b7573673d414f76566177333867573437455f39394a326a683746384149573439223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f7320287765627369746529202d2057696b6970656469613c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f656e2e77696b6970656469612e6f7267202623383235303b2077696b69202623383235303b204178696f735f2877656273697465293c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4178696f7320287374796c697a6564206173204158494f532920697320616e20416d65726963616e206e65777320616e6420696e666f726d6174696f6e207765627369746520666f756e64656420696e203230313620627920666f726d657220506f6c697469636f207374616666657273204a696d2056616e64654865692c204d696b6520416c6c656e2c20616e6420526f7920536368776172747a2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e66616365626f6f6b2e636f6d2f6178696f736e6577732f26616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a415a656751494178414226616d703b7573673d414f76566177306e6d4a4834444273432d317679714158357832346a223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d20486f6d65207c2046616365626f6f6b3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e66616365626f6f6b2e636f6d202623383235303b202e2e2e202623383235303b204272616e64202623383235303b2057656273697465202623383235303b204e6577732026616d703b204d6564696120576562736974653c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e5468657265206973206e6f207761792052657075626c6963616e732063616e206368616e6765206269727468207261746573206f7220637572622074686973207472656e64202623383231323b20616e642074686572652773206e6f7420612073696e676c652064656d6f67726170686963206d6567617472656e642074686174206661766f72732052657075626c6963616e732e206178696f732e636f6d2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c6120687265663d222f75726c3f713d68747470733a2f2f7777772e68626f2e636f6d2f6178696f7326616d703b73613d5526616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d43467351466a4161656751494168414226616d703b7573673d414f7656617730314959735a763273624653415f745778396f6a6850223e3c64697620636c6173733d22424e656177652076766a774a6220415037576e64223e4178696f73202d204f6666696369616c205765627369746520666f72207468652048424f20536572696573202d2048424f2e636f6d3c2f6469763e3c64697620636c6173733d22424e656177652055506d697420415037576e64223e68747470733a2f2f7777772e68626f2e636f6d202623383235303b206178696f733c2f6469763e3c2f613e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d226b43725954223e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e3c6469763e3c6469763e3c64697620636c6173733d22424e656177652073337639726420415037576e64223e4b6e6f776e20666f722064656c69766572696e67206e6577732c20636f7665726167652c20616e6420696e7369676874207769746820612064697374696e6374697665206272616e64206f6620736d61727420627265766974792c204178696f73206f6e2048424f2068656c707320766965776572732062657474657220756e6465727374616e642074686520626967207472656e6473a02e2e2e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c6469763e3c64697620636c6173733d225a494e62626320787064204f3967356363207555504769223e3c64697620636c6173733d226b43725954223e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e3c7370616e20636c6173733d224643557030632072514d516f64223e52656c617465642073656172636865733c2f7370616e3e3c2f6469763e3c2f7370616e3e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a7326616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454145223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206a733c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6a73223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454149223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732068626f3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b68626f223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e6726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a364241674245414d223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73206d65616e696e673c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b6d65616e696e67223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76756526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454151223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207675653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b767565223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b666574636826616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454155223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732076732066657463683c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b76732b6665746368223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e617469766526616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454159223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f73207265616374206e61746976653c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b72656163742b6e6174697665223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f7726616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454163223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732074762073686f773c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b74762b73686f77223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c64697620636c6173733d22783534677466223e3c2f6469763e3c64697620636c6173733d2258374e545665223e3c6120636c6173733d2274486d6651652220687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e26616d703b73613d5826616d703b7665643d326168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735131514a3642416742454167223e3c64697620636c6173733d22616d33514266223e3c6469763e3c7370616e3e3c64697620636c6173733d22424e656177652064654976436220415037576e64223e6178696f732063646e3c2f6469763e3c2f7370616e3e3c2f6469763e3c2f6469763e3c2f613e3c64697620636c6173733d224842544d366420585337794764223e3c6120687265663d222f7365617263683f69653d5554462d3826616d703b713d6178696f732b63646e223e3c64697620636c6173733d22424e65617765206d41646a516320754565633320415037576e64223e2667743b3c2f6469763e3c2f613e3c2f6469763e3c2f6469763e3c2f6469763e3c2f6469763e3c666f6f7465723e203c6469763e20203c64697620636c6173733d225a494e62626320787064204f396735636320755550476920426d50357466223e3c64697620636c6173733d226e4d796d6566204d5578476264206c794c776c63223e3c6120636c6173733d226e424445316220473565466c662220687265663d222f7365617263683f713d6178696f7326616d703b69653d5554462d3826616d703b65693d704d524f586250624472505339414f426d4b5059425126616d703b73746172743d313026616d703b73613d4e2220617269612d6c6162656c3d224e6578742070616765223e4e657874202667743b3c2f613e3c2f6469763e3c2f6469763e203c2f6469763e2020203c6469762069643d226d436c6a6f62223e3c6469763e3c6120687265663d222f75726c3f713d68747470733a2f2f6163636f756e74732e676f6f676c652e636f6d2f536572766963654c6f67696e253346636f6e74696e7565253344687474703a2f2f7777772e676f6f676c652e636f6d2f73656172636825323533467125323533446178696f7325323532366f7125323533446178696f73253235323661717325323533446368726f6d652e302e36396935396c326a306c336a36396936302e3831316a306a372532353236736f75726365696425323533446368726f6d652532353236696525323533445554462d38253236686c253344656e26616d703b73613d5526616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d434673517873384343475126616d703b7573673d414f765661773338654a695f306b32366743644b726d315057417979223e5369676e20696e3c2f613e3c2f6469763e3c6469763e3c6120636c6173733d226b73545534632220687265663d22687474703a2f2f7777772e676f6f676c652e636f6d2f707265666572656e6365733f686c3d656e2d434126616d703b66673d3126616d703b73613d5826616d703b7665643d306168554b4577697a76597a4173766a6a4168557a4b58304b4851484d4346735135665543434755223e53657474696e67733c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f707269766163792f3f66673d31223e507269766163793c2f613e3c6120636c6173733d226b73545534632220687265663d222f2f7777772e676f6f676c652e636f6d2f696e746c2f656e5f63612f706f6c69636965732f7465726d732f3f66673d31223e5465726d733c2f613e3c2f6469763e3c2f6469763e20203c2f666f6f7465723e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220686c3d27656e2d4341273b2866756e6374696f6e28297b76617220623d746869737c7c73656c662c643d2f5e5b5c772b2f5f2d5d2b5b3d5d7b302c327d242f2c653d6e756c6c3b76617220663d646f63756d656e742e717565727953656c6563746f7228222e6c22292c673d646f63756d656e742e717565727953656c6563746f72282223736622292c6b3d672e717565727953656c6563746f7228222e73626322292c6c3d672e717565727953656c6563746f7228225b747970653d746578745d22292c6d3d672e717565727953656c6563746f7228225b747970653d7375626d69745d22292c6e3d672e717565727953656c6563746f7228222e736322292c703d672e717565727953656c6563746f7228222e7822292c713d6c2e76616c75652c723d5b5d2c743d2d312c753d712c772c782c793b717c7c2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a28213129293b66756e6374696f6e207a2861297b6966286b2e636c6173734c6973742e636f6e7461696e732822657362632229297b76617220633d6b2e636c6173734c6973742e636f6e7461696e732822636873626322292c683d6b2e636c6173734c6973742e636f6e7461696e73282272746c73626322293b612626286e2e7374796c652e646973706c61793d22626c6f636b222c633f28672e7374796c652e626f726465725261646975733d2232307078203230707820302030222c6e2e7374796c652e626f72646572426f74746f6d3d2231707820736f6c69642023444645314535222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302030223a223020323070782030203022293a6b2e7374796c652e626f726465725261646975733d683f22302038707820302030223a2238707820302030203022293b617c7c286e2e7374796c652e646973706c61793d226e6f6e65222c633f28672e7374796c652e626f726465725261646975733d2232307078222c6e2e7374796c652e626f72646572426f74746f6d3d226e6f6e65222c6d2e7374796c652e626f726465725261646975733d683f2232307078203020302032307078223a223020323070782032307078203022293a6b2e7374796c652e626f726465725261646975733d683f223020387078203870782030223a22387078203020302038707822297d7d66756e6374696f6e204128297b672e717565727953656c6563746f7228225b6e616d653d6f715d22292e76616c75653d753b672e717565727953656c6563746f7228225b6e616d653d6171735d22292e76616c75653d22686569726c6f6f6d2d7372702e222b28303c3d743f743a2222292b222e222b28303c722e6c656e6774683f22306c222b722e6c656e6774683a2222297d0a66756e6374696f6e204328297b773d6e756c6c3b69662878297b76617220613d222f636f6d706c6574652f7365617263683f636c69656e743d686569726c6f6f6d2d73727026686c3d222b686c2b22266a736f6e3d742663616c6c6261636b3d685326713d222b656e636f6465555249436f6d706f6e656e742878293b22756e646566696e656422213d3d747970656f6620647326266473262628612b3d222664733d222b6473293b76617220633d646f63756d656e742e637265617465456c656d656e74282273637269707422293b632e7372633d613b6966286e756c6c3d3d3d6529613a7b613d622e646f63756d656e743b69662828613d612e717565727953656c6563746f722626612e717565727953656c6563746f7228227363726970745b6e6f6e63655d222929262628613d612e6e6f6e63657c7c612e67657441747472696275746528226e6f6e63652229292626642e74657374286129297b653d613b627265616b20617d653d22227d28613d65292626632e73657441747472696275746528226e6f6e6365222c61293b646f63756d656e742e626f64792e617070656e644368696c642863293b783d6e756c6c3b773d73657454696d656f757428432c353030297d7d0a66756e6374696f6e204428297b666f72283b6e2e66697273744368696c643b296e2e72656d6f76654368696c64286e2e66697273744368696c64293b723d5b5d3b743d2d313b7a282131297d66756e6374696f6e204528297b76617220613d6e2e717565727953656c6563746f7228222e73637322293b61262628612e636c6173734e616d653d2222293b303c3d743f28613d6e2e6368696c644e6f6465735b745d2c612e636c6173734e616d653d22736373222c713d612e74657874436f6e74656e74293a713d753b6c2e76616c75653d717d6c2e6164644576656e744c697374656e65722822666f637573222c66756e6374696f6e28297b66262628662e7374796c652e646973706c61793d226e6f6e6522297d2c2131293b6c2e6164644576656e744c697374656e65722822626c7572222c66756e6374696f6e28297b4428293b66262628662e7374796c652e646973706c61793d2222297d2c2131293b6c2e6164644576656e744c697374656e657228226b65797570222c66756e6374696f6e2861297b713d6c2e76616c75653b793d21313b31333d3d612e77686963683f4128293a32373d3d612e77686963683f284428292c66262628662e7374796c652e646973706c61793d2222292c713d752c6c2e76616c75653d71293a34303d3d612e77686963683f28742b2b2c743e3d722e6c656e677468262628743d2d31292c452829293a33383d3d612e77686963683f28742d2d2c2d313e74262628743d722e6c656e6774682d31292c452829293a28613d71293f2870262628702e7374796c652e646973706c61793d2222292c783d612c777c7c4328292c753d61293a2870262628702e7374796c652e646973706c61793d226e6f6e6522292c7a282131292c4428292c753d22222c793d2130297d2c2131293b6d2e6164644576656e744c697374656e65722822636c69636b222c412c2131293b702e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e76616c75653d22223b702e7374796c652e646973706c61793d226e6f6e65223b7a282131297d2c2131293b6b2e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e28297b6c2e666f63757328297d2c2131293b77696e646f772e68533d66756e6374696f6e2861297b6966282179297b4428293b303d3d615b315d2e6c656e67746826267a282131293b666f722876617220633d303b633c615b315d2e6c656e6774683b632b2b297b76617220683d615b315d5b635d5b305d2c763d646f63756d656e742e637265617465456c656d656e74282264697622293b762e696e6e657248544d4c3d683b762e6164644576656e744c697374656e657228226d6f757365646f776e222c66756e6374696f6e2842297b422e70726576656e7444656661756c7428293b72657475726e21317d2c2131293b683d682e7265706c616365282f3c5c2f3f623e2f672c2222293b762e6164644576656e744c697374656e65722822636c69636b222c66756e6374696f6e2842297b72657475726e2066756e6374696f6e28297b743d423b4128293b4528293b4428293b672e7375626d697428297d7d2863292c2131293b6e2e617070656e644368696c642876293b7a282130293b722e707573682868297d7d7d3b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b66756e6374696f6e20622861297b666f7228613d612e7461726765747c7c612e737263456c656d656e743b612626224122213d612e6e6f64654e616d653b29613d612e706172656e74456c656d656e743b61262628612e687265667c7c2222292e6d61746368282f5c2f7365617263682e2a5b3f265d74626d3d697363682f29262628612e687265662b3d22266269773d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e7457696474682c612e687265662b3d22266269683d222b646f63756d656e742e646f63756d656e74456c656d656e742e636c69656e74486569676874297d646f63756d656e742e6164644576656e744c697374656e65722822636c69636b222c622c2131293b646f63756d656e742e6164644576656e744c697374656e65722822746f7563685374617274222c622c2131293b7d292e63616c6c2874686973293b3c2f7363726970743e3c2f6469763e3c212d2d206363746c636d2035206363746c636d202d2d3e3c746578746172656120636c6173733d2263736922206e616d653d2263736922207374796c653d22646973706c61793a6e6f6e65223e3c2f74657874617265613e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b76617220736e3d27776562273b2866756e6374696f6e28297b76617220713d66756e6374696f6e2861297b76617220633d77696e646f772c643d646f63756d656e743b69662821617c7c226e6f6e65223d3d612e7374796c652e646973706c61792972657475726e20303b696628642e64656661756c74566965772626642e64656661756c74566965772e676574436f6d70757465645374796c65297b76617220623d642e64656661756c74566965772e676574436f6d70757465645374796c652861293b696628622626282268696464656e223d3d622e7669736962696c6974797c7c22307078223d3d622e686569676874262622307078223d3d622e7769647468292972657475726e20307d69662821612e676574426f756e64696e67436c69656e74526563742972657475726e20313b76617220663d612e676574426f756e64696e67436c69656e745265637428293b613d662e6c6566742b632e70616765584f66667365743b623d662e746f702b632e70616765594f66667365743b766172206b3d662e77696474683b663d662e6865696768743b72657475726e20303e3d662626303e3d6b3f303a303e622b663f323a623e3d28632e696e6e65724865696768747c7c642e646f63756d656e74456c656d656e742e636c69656e74486569676874293f333a303e612b6b7c7c613e3d28632e696e6e657257696474687c7c642e646f63756d656e74456c656d656e742e636c69656e745769647468293f343a317d3b76617220723d652c763d736e3b66756e6374696f6e207728612c632c64297b613d222f67656e5f3230343f617479703d63736926733d222b28767c7c2277656222292b2226743d222b612b2822266c6974653d312665693d222b722b2226636f6e6e3d222b2877696e646f772e6e6176696761746f72262677696e646f772e6e6176696761746f722e636f6e6e656374696f6e3f77696e646f772e6e6176696761746f722e636f6e6e656374696f6e2e747970653a2d31292b63293b633d222672743d223b666f7228766172206220696e206429612b3d632b622b222e222b645b625d2c633d222c223b72657475726e20617d66756e6374696f6e20782861297b613d7b7072743a617d3b77696e646f772e77737274262628612e777372743d77696e646f772e77737274293b72657475726e20617d66756e6374696f6e20792861297b77696e646f772e70696e673f77696e646f772e70696e672861293a286e657720496d616765292e7372633d617d0a2866756e6374696f6e28297b666f722876617220613d2b6e657720446174652d77696e646f772e73746172742c633d782861292c643d302c623d302c663d302c6b3d646f63756d656e742e676574456c656d656e747342795461674e616d652822696d6722292c6d3d2226696d6e3d222b6b2e6c656e6774682b22266269773d222b77696e646f772e696e6e657257696474682b22266269683d222b77696e646f772e696e6e65724865696768742c413d66756e6374696f6e28682c7a297b682e6f6e6c6f61643d66756e6374696f6e28297b623d2b6e657720446174652d77696e646f772e73746172743b7a26262b2b6e3d3d66262628643d622c742829293b682e6f6e6c6f61643d6e756c6c7d7d2c743d66756e6374696f6e28297b6d2b3d2226696d613d222b663b632e6166743d643b7928772822616674222c6d2c6329297d2c6e3d302c423d302c673d766f696420303b673d6b5b422b2b5d3b297b766172206c3d313d3d712867293b6c26262b2b663b76617220753d672e636f6d706c657465262621672e6765744174747269627574652822646174612d646566657272656422292c703d7526264e756d62657228672e6765744174747269627574652822646174612d696d6c2229297c7c303b752626703f286c26262b2b6e2c70262628673d702d77696e646f772e73746172742c6c262628643d4d6174682e6d617828642c6729292c623d4d6174682e6d617828622c672929293a4128672c6c297d647c7c28643d61293b627c7c28623d64293b6e3d3d6626267428293b77696e646f772e6164644576656e744c697374656e657228226c6f6164222c66756e6374696f6e28297b77696e646f772e73657454696d656f75742866756e6374696f6e28297b632e6f6c3d2b6e657720446174652d77696e646f772e73746172743b632e696d6c3d623b76617220683d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e74696d696e673b68262628632e727173743d682e726573706f6e7365456e642d682e7265717565737453746172742c632e727370743d682e726573706f6e7365456e642d682e726573706f6e73655374617274293b7928772822616c6c222c6d2c6329297d2c30297d2c2131297d2928293b7d292e63616c6c2874686973293b7d2928293b3c2f7363726970743e3c736372697074206e6f6e63653d224f77774e4a497141533843544d31352f4351595531513d3d223e66756e6374696f6e205f736574496d6167657353726328652c63297b66756e6374696f6e20662861297b612e6f6e6572726f723d66756e6374696f6e28297b612e7374796c652e646973706c61793d226e6f6e65227d3b612e7372633d637d666f722876617220673d302c623d766f696420303b623d655b672b2b5d3b297b76617220643d646f63756d656e742e676574456c656d656e74427949642862293b643f662864293a2877696e646f772e676f6f676c653d77696e646f772e676f6f676c657c7c7b7d2c676f6f676c652e6969723d676f6f676c652e6969727c7c7b7d2c676f6f676c652e6969725b625d3d63297d7d3b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f6a7065673b6261736536342c2f396a2f34414151536b5a4a5267414241514141415141424141442f3277434541416b474277674842676b494277674b43676b4c445259504451774d445273554652415749423069496941644878386b4b4451734a4359784a7838664c5430744d5455334f6a6f364979732f52443834517a51354f6a634243676f4b4451774e47673850476a636c487955334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e7a63334e2f2f4141424549414367414b414d4249674143455145444551482f7841415a4141414441514542414141414141414141414141414141414267634243414c2f784141734541414241774d434241554541774141414141414141414241674d454141555242694548456a4642453147426b61456959634852464256782f3851414751454141674d424141414141414141414141414141414141514944424155412f385141485245414177454141674d424141414141414141414141414141454341784a4242424d7842662f61414177444151414345514d5241443841754e655843416e4a3644725735706631746450362b794f6870584b2b2b5044622b3265703971664f48644b56325236364c4b4862364632343858744c57366139456b6d64346a4b696c5254487944397763394b66597237556d4d3149595746744f6f43304c4232556b6a4949394b3566313559333151303374686e4d6474774d504c386c455a542b765565645666675271495858536f746a7a67564a7468434d5a33384d377039747836553232667274794a342b767479566c506f7242306f71496e4d5053705a7275354766656a486279576f333044486452362f67565272784a6369323139356c70546a6752394345444a4a376256504e4c574b5a4c76726239776a504962625558564b6451527a4b37664e58764334787931726f797630656438635a58305a4470526d586f682b7879414f61537965645248527737672b687837564265476c346530667841615a6d6b7474716456436d4950626647663841516f44357271554441785541343161496e6a564b62725a59456951334f547a752f7741644255554f4a786b37644d6a487a564f716455365a705a776f68536a6f45644b4b576548647a6e584c53634a64316a5078357a4b664265513867704a4b647562667a4739464b4f4d7441414859555555414734464742525252434742355555555678782f2f32515c7833645c783364273b76617220693d5b2764696d675f31275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414945414141424943414d414141446d6d575965414141416f6c424d5645556949694c2f2f2f38414141415a47526b644852337537753751304e41534568494b6d654156465256675947414f4467352b666e3757317462382f507a643364314c533073784d5447506a342b656e703675727134714b69705a57566d30744c544477384e766232382f507a2f303950546a342b4f38764c783364336547686f596a484255556572416545414137676138416d657133312f496a46674d586135704570654d416b64375336506f61585952666b376f4f6a63346453474d476e2b6f564141416a445141664f6b77684c6a636b4141416256485a4d6b39507941414143636b6c455156526f6765325732334c694d41794748646b4a635135415349434568435464412b774a324c624c2b372f61536e4b67334f4f5a6e646e526431484c78575039746e384a6c42494551524145515241455152442b41347a57326a794d48476a3661397a6e4d3044306662314e30356e784b7142496b6d53706c55356f744e4d2f6d7655475a355149564c614e366e49444c6d74716d374c4d647542527761665051524345566b474634787a504452454743326944494d633855474a414c48597036656e647450497034637458334447624b656877334550615577616c777943495145456533476c5342634e747376636d3458434d762b47474853697a6f353033674b6d4441517772674a7254686537675331764d6359676958756a4c43324d63662f394247326f464b787a7a4d7544444f77554e35616f4c6f2f6f35793654374b5145324e4b536542507938785045766c395135674e78677a4b5341336d434c486a5441463751636e414b6f4275767246635a54484d66734144532b556650626730384b5345374231773137537235685439617235554e3150696e6766496b764c37387a50697157576e4d33475374594f3463517475645046704d5238384b54684665386773766261454e33566e594350346854554842644f67556b726f5a37626379564879652b6f51744f72316a30624331584456514a44362f51756c54634c6b704949616c797168612b732b6435666345724f492f4b724e45414c62416670764d354252326e785a57574c5a4c73686d67414d4d6e48367a7a4a4f39727739416344324f4b6550643345766e2b73526d35417051565930726c4436685768776c7277706542775242396552347a342f6b4f36434e634673395170534e6c355965344d6d5042547446585a6565764c65415778693236746f48646d6d4538393052547452316465676448686255493934326d7747313075787750486d6973647a613563515554544e354f355351766168507870706e4c73646a3671635478667239663361574c364c4d75474e5956416b634a3551316c67557932365254335947532f4457716a7262652b704934334966574a6e6941733577726e4c6f6748774e387273746b3754377858724a623867434949674349496743494c77442f6b4c6b327368546d377038716741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3233275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645584d4141442f2f2f2f4b4141444c4141442b2b2f763435655879304e44514e7a666f72713754516b4c72744c54696b5a484d4a53584b44772f34346548484141446f7036663939766235367572383866483031396676784d54676834664f49694c61626d37636433666b6d4a6a78793876575631666e6f4b445453456a55546b3759596d4c4e4768726666332f514d444475766231676d3047614141414758306c455156526f6762576234614b714b68434662636a4b4c44484e637165563758722f5a37774774484d424b70347238394f5154324341575441464732584868635757595855706e337661373571345341363249734e325349713432584861503875344374385642467a5a31564a36382f50675243774941736149732f4b6154634e6c61636d346546395538506a5a484261424d6b714e316d31755442622b4b30507265446d68645a63316f2b373762585852706864346a446a51354376385562676969783033336d387236414e7548695a4f764c452f4f65475746374a5830414d383952656e327346356c67333176573844486b365733756751387a46653367785659414b76773539487a566a37546d507451324131396e6e384d6779386a725950674e6c75654c686265367947654b45447277754d4866716a48484b636376534441626a614f78546e6d333765316f585841526f443369354b6e477654684c4665337145304b6e67766e58385039394c3442376a55696a4d57586174566358706f79317a762f452f3043756965726c61766e346436486b704c506e4e7269794e4f74304a3953486f4734726e50627a51586f4470524c592f6c34715758787736682b727442464d38756b637165525256396e48362b377255395734444c573763384f3364724c634364714c44796374776736753576477a4b4279647053616162325876416e74724d3273514967443858444d4a466c617a4c47734470333637794c702b6b39716f2f79363646544c58753261735633524d537a2b4261565969535037592f616a722f64473156754f574e734a346862614f4c5446705a63756b5834567644614375695734612b6665516856306e74364c392f393041372b7531507942707234612b6c5563464b7133674d6933494c452b68762b6a64676645476f5577456738496a454e30472b3470596b4958416d497245414f6a304d4c4a5a44747865386c64756f556f4278505a3241673136495170366b5265765544412f3661426d5272345767704e44454b3359464d5241727577486235464835547779676253326f2f55506252424744417a4b6b52554f494f4446672b445569524b464a446b667345494a585467476f65342f354457336467384b696d41646b394d2b70557a39794172446c4d416759557638746b734b507732423359397047786c6734436c593973594a506d6f5475513751496c44394f6a47314175463276636974324241656e37345168512b6367524e3732584f394459674d654163726e4157423745787478413556304a646d7271455269516d41596f48336a75452f676a436d4b3038657352474f78465a495842307237774347534e384a73664b4e6b632f41454445744e673159337631444c72436368494e43666d38477a704436693233514d63644644734561695544473746753556486f4654417141537057666f444b744652595a5261654151714266794c3673596a554a3263354f673346342f4134476d4a55742f507641476c6b74485554656b524b4a58526f734374654f5552794e6269425467325a48655051435658387a31757852642f514b566b674d42714f506561462f694a55753851382b424a78377a41674552732b656f2f444a77644b4b505565793978426941396f454b706276712f3650384464316563427161366d526e347a4c4842596d7145634877316377767871456b706d5733506f664d73774d5664587a34586d72715a47336a456f4e73537063344d314134564c65706d6271424e795352506a304474356b5a47716462626e4c6d416d674b5736755a70385a765a674158347a554e737862596c6454626745734931715752513363774d58427a4252355477683256325a714332664d717a31462b6a69544d434678685a53415673584f504f436453556a4e694b4e336f5435775271536b61637752386172596c7a416e55464c4657785436436d5a4f5235757a5931484941702b4d4967634d6b4d4a625049344a6b445542755a51534436694f304d33674749303273456d454f594c34562f6a74757a425a68324c483571517a414d314f3970684d6948726467473542335438794847674f676a37436165645a74744177375a4b44416a773866674c4856326f4f59335574313074754c3567526e653030683138333168667544696851366852616b65674b69413154334e3766504d4278447661526775715436416d674a4764654d46695063303671723336524734534347456b3165393667782b4b6c43635347493269415834395248426b506c4c612f6f486f4c7a4777767966737757493236343862792f454d3345496b4a783731314c4e784257734a6c335746694157495855477a786e6a49764249554479477653595073554f494a5042693632505961536f314a3637726b336c6b79347a304673304f6d4f4245396e77767a572f6b6c554b75726d74776f6f34416c31712b45612f7335664463464c6f427732506a4f67397871316f54373953547449634b5746374179797069644645654a4c3157706555443239664b335a37304d6b334a554b507550784d74556d58314a776e4c596b5941454e6853495a536875676b5933654c5836334c5455305a356f566335624e594d476d6b587266506679576e4746374d6f6d38537a547772567158754839615231386d6c4150512b696177574e767339752b5351672f78336757525777586b47786d414a5561553239466f356c62516f56366335546556674474724b6b4d5864355a5434467950686754716d777a5a446a55435136794a56483535354644657a566b7a76397275416d46796e48356c467035434c5a652f566d397878474a3756477565484f71577565666e62694669522f76443672367a694d7a6c45364957462b6357783257696f7165386266377833613856746a55586c7847547845786e64362f366d674e5349656c476c334f4437704c5262626271746a6b76334466783765365a7176754978327a3374357262522f54667748444f747262744e7643415541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f33275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416456424d5645584e4742332f2f2f2f4a4141444d414144373850444d44685856556c4c4d4642726e73612f37382f505556316a76794d62504953583032747276797372464141446a6b35585150554c4d42512f787a39444d41416a36362b765351304c2b2b76726e724b7a32354f5471754c6661654866585a4754737672375a6348445961576e6669597664666f48504d54446a6f364854536b765350446e6a6d70765a704856754141414461306c455156526f67653258323361714d42424177345159735642705141533543496a2b2f79636553494a415346307439727a4e6672484e5a546245535449536769414967694149676941496769414973676b6d597141514338614e446a3567616541324c4d4f48475749646e43625876413772706a6f5145504f78784f395a7a423461664b342b5448547a776769304c664a4742743852594c6f314f6a6b4b4c3768387a5a5277433370536d466169692f71474c49597957484d5a65353947547545615a57636433413175766c59366337496948683845396b5044783079343834615747414a6e545342554c7832466774646e5938776c595376685949692f46626f7668653563534c6c6c56456774517363723645736832495673496153352b58707a59526e6d6564366b6d57342b4a2f53566b454a6d695a5574684841596661653036594f48355578344261423938736230324f37564d4f386758676e4a555350484a7570766d43397058476a6635533644437771515a3072495366794d4b4b4251442b2f434b2b4759467141656a7131377756566871696e704f6656444b61526b546e794d354e413966555049495657724449766748496f6d4a72526343416e7a737a484d5a69487235462b6e75316a47376e644b502b70734e677048657a594c395174326c4b7942306c6b3133527731636276514d535a4f634f7173684379525231314e7477726a5175367473583042726464434168394b7446564935596f477134746a6a4c312b696c784f3844634c5a614b6e31685674413475513357574f4a5275467a4a636e5832564c476461644c4549563646787346616f63574f304a4f6162772f6c346f4f696b6b7470775277354833763453326e486b6c394137764c656d5864556b50746955563356386b5457354e6d7353574e46444c6262483561465062596d2f64467235745736674a356561545268326c67572f37456d6c704f646f365479334a357150744d4877366865314c48413456737731554e66444f625347664f4c4f7436544448664962453053665464754644686d6a57526e6c314c5a766952463265795273584d4c2f4c532f78636d5562654d706134792f654c56526e2b654b6645494e4449494e357559525351317054514734466e4f674856785849774c364b456876326969464a35344e7734544c70447049756f422b744c4f646f586370422f716e48756e5432466e2b32587068552f466e4b69612b5567684b4d4d666b777535366b517a737048474b625065746f564d714f5663474950507859534e6c586e575271476a314c566a665a5350306a55446a4b46743138492b2b71764e4f4e2b49335276342b2b3474345345482b755459394c454b3245354658656d4d41586a42356b7058505953656a63435a4a5536376937425365616d6d333355414e4f39435858304f534f7149536d48542f45554476316c4e77714e3371485568766f6a6b2b2f746e614a39432f72484b764375714b7271756d75424c6737634958466e304434564271597838742f6e453571394b735239642b324446776d647651706e61702f5a6a7665335563475a72654241454152424541524245415242454154354466384152776f2b4948597561487741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f35275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414555414141424643414d414141417255397362414141415946424d564555414141442f2f2f2b4b696f724f7a733553556c4c613274716e70366672362b7648783866382f507a7a382f4e61576c712b7672342b506a373239765a7763484376723639396658316b5a47524d544579676f4b41774d44415044772b31746257616d706f6749434156465255714b6971546b354e455245527061576b354f546d4d703246774141414239456c455156525968613257366136434d4243465757565241554655334f3737762b55464539716d6338704d577336764575424c4d3265324b4e354455534c53536631775171386a6d5470467959522f494f574b4d7670446e73554b4b57372b6c4c4f3653756f506958704675516451486f7079436143552b317255424667304e53736c44376a4b50686131697649496f4f6a3862774d6f2b31696b382f2f7044376d70713452594e4f6a6d456b424a464157336f3965677a3333716b67704c2f4957554d74626e613879712b6f4f55334b436b504b57476b4c6b2b39455042553742466333336f454e55383551417072554535386843485258654430764951522f366e4275584151796f4969577144346d39526246427946754c492f3936676a41304c69612b51636a556f47513978744b695451526d366736334b707277677054446a516a5353752b447633473857585777497a7639736d2f4b774b5868456a2b30734e3657304b572f3374323652477666702f792b5351443558365730497a6e3947695530706653696454656c384b4b51366662616f4737486f374548356b43727947644666472b49316f742b37574554794837636f527353696e76306c79596c4963486d4c79505770696f6d6c43495972586c476e2f766a54373447484f4b624932672b587336542f347847394474526663415555624e487170456e636b474f4c576f764e4a47366f674a447a6d672f4c67324334596f745550357a5049386b774b707a2f4b71435262502f424c55706c7139436934365a46432b584f513271592f35504b2b5569306f6d4b4c50757239334745465659547a58776430336c7745467545527259654e7a434b38464f6946456d775956425675555471676f686256514d696b5034674769555766444d6859746951646974632f7555735465446a676a616741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f37275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141417146424d56455541414141795a737a2f4d77372f2f2f2f304e42466246776f70544a497a614e424f546b374c793873725973744f6564497156616f735a644258614a442f4141442f4c77436d57452f4e4b51757071616e2f61463956664e4c4d31753854574d6b2b506a372f7a4d725330744b4868346663334e79337437664377734a3565586d546b355072362b76464b51314b45676767505855794d6a4b656e7035746257332f3239556348427a2b764c622f66572f2f6548442b7070372b366555724b7973524552466a5932502f5755784663744259574668634477426c5555395759486b6636744c75414141427a456c455156526f67653359363036444d42694134627036574f64685536596955416271594e4e4e6e4b4c652f35324a5543627041637570662f7a65784d515136324f686443424345415242454152424550516675314a302f3944686c7a36654b554d6a525964484863446a4532564467516571414151515141414e67444e6a344351506a615a7955674358646c365366657453516f676678633341367a794556334f704b49414f7a725057464f2f7a466d746463484a614445485a31354e736b674a496967472b673674353478596758756e4d6b474235555173776d324e37454339616750685a4f4b6e36494259587a392b6765464a72514e393141387572584d6357494e37775531534374447751684d6f70616f4262585a442b486c6d57496d6b437672427a796938624657685644746c4d444e4d47344a62396c573141564f34422f454b74417a64647741524c447634427a727541355172674c2b4a77494c7335516d4f677a30596241796d41413131446334754762545838396a333466656962416c3032324455466c7338334f304f677863593672326241674133464166656a76594c59693232453176624f33582f67383364687a324232317a6d4f56336d6b6b5479323951787943536430594a434b337042674b486b4d48684b6b306c654c57764474726b67586a464c693565737a3942782b67366b4633322f79304778614e4e4945732f737753654d343371574a696c4f384839376d36623979537a66764a6d4152674141434347414e474a47663150746e3732435461762b62714f716a43336975444632712b687933372b74435759647051424145515241455152436b33546552454559374c6f71374d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f39275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414267414141415941674d414141436447645672414141414446424d5645564d615846436866524368665243686654307443505a4141414141335253546c4d4167464a456b47784e414141414c306c455156523441575041447867647742543342544446394155697568644336574e4b2f762f2f2f792b55676772436c53413037455756676c6d4546774141356559534578654377696741414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3131272c2764696d675f3231275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414649414141425343414d4141414477386e4f70414141415946424d5645582f2f2f3933476b6c3247556a6d3074794b4e324a39496c43494e46393548557a31376648372b5071424b46584f714c7a392b2f796556337672322b547a36652f446c61336276383678655a6152516d75565358436a59594c77342b7135686143454c6c716f615972486e62505875636d2b6a71657262343751726344697939636a4536764241414144756b6c455156525968653259323961794b6853474253564555544655464866336635632f696943536c562b4e64624a4737306b6a6841636d7a41306142442f39394e4e502f325052754e434b36616345767948764d7130752f7778597a6a744c2f34513371425746487848374b72562f73746b6777614c506b48534b3049344531667731556847686939544d6235416b5662766d496d4753666f636b556f31316b62426c38566449496c7331314558654a416d2b51524c5772754d63354b526439464d6b59516e306b5a76546634714d4d7742385a504157535568634e49304b56584953713339486b727958517356715656565a5051705a357551724a4733536a69634951794f4d576a354f78546262784a53475349384475474e61386755796c686b43436752324c662b5362467058576c52346b5832474e3758506b666d59484841574331717865484a546e5478556a354f6e794c7a475a3043396277767a7a38696965307063686b6e365a79526c79424b6833696667626750506734622f44526c794f7835463958325154497856757739454d696a71534f6d4754464d626166467a4a4758324a4b4d68584632636b714c5039715633684f616855726d5a44394551626a7048787257317348644370686e4e544a446e76717450723130394e33596a6565686c323245372b38673330564f3257377a775937326b6432774f6f6677724d754e61776774706153782f6853526e534a4a764b72795a552f5157535949317543376d53784b4f3731665a424568634b68516b7a7675686a717758506b655767537151394356534f5754597933764745777a322b486d4f6c4d486d5975644930737970364b70494a553177544574506b56536f7177456536436d53354f6c59335235676235426b564568594e536449476772755a2b424c794870424a76306a4d7062384c4c764261386a566367385a6938514871765769396e595243556269752f7141344a4547634d493756724b337271365273493739674c7a74524c6a517370475644626b53506152626b566c78524d616473385132593331596248483748726b3630534e79746f7545714376644634494c4d533431306a4f63375847337051416a2b52355a726b6a76654d686f7265364f784743346b6a62555879794f546d544c78455032337964376c64793267533679794179796e59394957796865707542742b3836525a71425a774f367572354771674236527074755755586269644873616b4b72726a6f544a736d47483437467543626d7a7a4a6a5a63465449336b50434b6a54544131415848704b4b2f5972484a33336d744f68724e2b794e6e5935466e4d3135735742687443376b344a64393469776e45326d355a48576e446577466e745237473235357661547a684433576e6959376844684b45504c7a4d4759615365394f493453334a554f63565568716738523042623677324459756462747531352f6745626d376b53396b64376b6a767176613063455a4d706a504c342b524d49652b567066566375636d2b684a4a792b7168546b436339633439793768584d54713332794e53767849413078694f4c58424e676a67532b584c412b72304249476263734243746d663641624d52646139674d436b672f526e627263464b4a6d5377426444656162474352307654554a3237626a5a796d63424c7256355436727371454a6c44623778437171756459582f7a596f74346634394f58783865656139645050776e39394e4e502f34332b41627851504c6a33385835594141414141456c46546b5375516d4343273b76617220693d5b2764696d675f3133275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d41414144785067523541414141636c424d564558754d794c2f2f2f2f74494144744a513334747250754d522f74496762306a346a326f3537744b5250744667442b2b666a326e706a796533502b39765838337433754c5272764f536a765244627550433334757258765545583936756e316c354c734141443731644c3372367636794d587861562f30683444367a63727858564c796347667857457a76537a2f35774c3378596c6a796447355a6e6968724141414745456c455156526f67653261375a616a4b424347465270554e49716d51324b4d332b6e37763855315569674b5a6e706d31396b357537352f2b675331486f47694b4d70326e454f4844683036644f6a51763667414c555578333564583970384c3362324f426a7343306164724b446b6873682f7777775336626848735272514433527a395a714162375558634173624f546f4f71674c6e3030534a577848496e563158413634324e776a3041377a754e71514a475750346d5349436a4d6b4a6677765051347247424f774531684231754e744c76414231306c773039612f315271627156583866664433727944586e3461546236747069314366545a616677626e6c555873657837644c50456970795646732b7a64644563306b51324e45674334786d5977477a2f493844726252783168487a3557374267583241566a614e2b682f36355462414e74437a646e7765754644486e4454434d6c6454394869766e786841612b2b3843512f48466e4733676241592f775061647a52594a675957564d46757773674772376e58724779435234686d597a6c2b4c46526f4a79313134304c715432346530542f6d506530696f4d70317070686c346e6476674e65766448496f7a2f7947517759703148357070656f4c474b31756a6c6b42526a6371564779546f5230436b2f4e4c546f69362f774b7757477a776a654163656d436e5a6532445167756c65343545415a6c56737067784770474641544735663734416b5662366f6d326146624179377a645450694b57457931454e7337644156494844364b5a5a424d507a5a5863594b3942526f394b73675651445472366f6d38594e4e50706245326a74495947352b57706764496936494e2f4577356f7636716b507a38446a716a65384751692b4e3832683237547937776c65686176667765434c594c725148515a47774256763036485a53386445483538564c2b7a4f7371733534764a4e594a4f6f4f6546674f694663785a64686c4e574b767543356352736f45716b7048417547495a44305a34596f493272664967524233684d323355574a6f437338654d326d7876514e304642457356725a636546485062695336794f734273454e703775465535754e626d745a484674414d517957476a6c646354614e37614b5a322f62446e7743477a79457444657251754843697a68354155592b75695a3969325236576731762b54614435644a686348556937672f4f6e33736d71485265394252682b47386766336c4c6c5634766f354e4345645634685242774c6b5565315843444749362b6e5347733258732b3268634878537348797451686d2b4a786c35344252767658496f4f452b55373938487470617849634f2f5235685a74567535536a734a315a39375558633269333271696e386a34476e2f3177502b334b3574625237526573705457544c6e555775437349483264446a685639614f57617150346b6a31443261356e6c6d36343074594b52746d73636c6d4f494430594d47665664743351515364726d4c4d55754e63382f5279774d42505658696c586d4534764d686b53544c5a2f582b56376f64716261415161706e4d364b634377536f3062504871734d6a634a6e667858317472536873413347397974632b564a366a4b6b655437596161774e634447314f385067484c7542303854515079334b4a4b6362504352324144756b6e36746f7152464570567a6164366944746b61387255654a536654684375454f6f6563535932344d617832374c7748787a4279546c76686b51764b36586c49636d666177623353307253467037395241434d723145552b644d78704c6564453231412f46536a474c7a794e5a724b5939516471544e384f446a6e717a44453543485a37626745436f516f5259796f3038377a65366e2b4138784f70537334326f717067314f684241373150674d67684176326b4a597153786e62426954796b466a53715a6f6c5061566863676146616963384857394e71414c434b366f536743564b32594279524d504f5355466348756d6a6d79786446456864534e4534326d4732416849755454586d6d453666456536544d706943574a4d4533615137684e6f464f63623165516c30626e4c614c51467a64635a2f69526c4c445a52547358486c7441624367662f446e45524c704448584e696a68573841795851484278766541314e38774b394157304673447764463973795a734151627962426d577035556171506a6e7a66704b6c36336e554d36365a2b594e4675425541574c4255687a4f766d4934524b36757249415949734a6c323073314949477053725449784d645868524c716f6b4936426b7979424b7276536647626861383773504b612b3030744b33595a69336845726e4d337270556c7a737258755a726f6b5959674469556c7978526167535144582b6a54327a4234394f5a4559546a577464566d474a654d3451437a573132356e327747336c366258486546636c5a6f32793673477a425664632b77754a616c583732736a5846782b6b5467697274586576667831394235745433705879324743375a553037376a30386f314e4e5a4a6739713845472f7368354731696d6b48456d34533551524e33306230563845326f4765766d6d376b4e4d52592f5155555866426c5659454c5332784a4d61724f2b726c794145497063753151424c57465a7252345476394c77476d70495957665973334e704a4a3769376279524e4c566f7a4c446f54684b7934387153664c4362357a4638356932555a4550562f7072693255326c7a346d3156324b4e33474f537472742b587a7753686b6f4d6a4e706a7448347458482b527847753653676948547030364e436851332b492f674a2f6948506855784b5a387741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3135275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141413156424d5645582f2f2f2f6a6641446a6567442b2f2f33696467442b2f6672686367447267514438367450366a774436376433392f5066376c41447468674431696744326a67443335395037394f763738655437307148706a525866625144716b437a3133735432343833686677446d6b555838324c50366f4450316c532f766d5437726b68623030375478794a767477492f737549626967436636776f58346e682f76304b726c6a546a6f6744583236642f77785a37686642726e706d3369677833747670546c6e316e6c70574c7172336a767a4c54706c32546e6a457a7670337a757771586f754a6278313862696e573770716f44706c46726b63786e707359336b6479626d6d555030723176347a4a4c367533583272453331707a6e796641416c4e7748774141414e4f6b6c455156526f67653161435865627568494749555143735931494d4574365578616e45474e6a77456e644e4c6b70625872372f332f536d7846657744464f2b3972653838343731546c3144466f2b7a66624e534b346b2f576c2f6d6954706a764c76417159732f6e634259325a6e36722b45705a704f6b6b526c3551572f46555a426d366e635361496f4468304c704d75383548666953524a33677a444b484175526c314f4657776c3166694f674730616861786e71536a47347362793965372f696269616276774e4b6b525365784a4659652f7268357537526c5054567a6430484b6244693334496f57566b5563496c7a2b4d72763339383833457553385948666378377965577239636a676c695630442f6b342f6d737370574f2b653636725264446d426c4e37785834786e785948424a5555314832372b2f6d67595169442b734a713647495768593651706c33346c366269527a706533793655424b452f4c6577752b54766e7138655a6d4b5a416a79357a4d6a562b495a325a63637039752f6e34414b62677133542f64664870554662363865586f537174526a3157585672304a554a434d7955484f3371395855584b71414b443038725a61415051554f554666674e30346b42617871614f46486d3272736b614d6942655953564b644d5665582b34656239453677364258633137724558465875376c494b466c4c487352385341565254446955732f54564f2f7142795541324d503133534d3230635142722f6533393138584973396658783457454a77726a37646656436c49414d6d2f333545574a6f48706363596f35544370383238644747756a6349742f75456a35782f76707841504b78302b5631504f7265586a337a667751706f7570616b687157456d56665148614e55745a45706b797168334d523566654d7932575435763071756c532b7239394f6e75426f4d4e325852353933674c2b6c773933433978543462705a4658783643564759627666475278475261684d364b514954554f465a7568426d524a477834474b4b6f5552717252386646677041516362547a393875734e34554131754a6d57614531414c4a64544f72474c6d66422b694a594e3065597955714268476f306f6556446c6a4261416c316e513152564c6a397838665079355269397930544366326852474954414253546f73713043635439377438315a68545670677730417a6e5256484d4936665256557a735757796f7a764c6d41373551514c59555a654d414e674555496f4e654743506a4b6a41744c756d754b6166485357367a47576353414942546778454a7754586b306855305855337373614d75507a6f636542706b57306d75365251455147515a4a5a756c5253783242794670756645386d666d39524f3434727237656a6d4c6f7742636c6f374973316b46513667665971382f424f4a6c6a38506550364a4f4b75386852697a434b73627859424d337975684e557151632b586c5330364b4e564d70756b666855366131413342654667475a764b6e6b647444424130494d696432677755646676707653475a3551785641426133535a3370584b774d6476426e464e2b4467744f616c54324151686a302f334673416c63795849616d6c646941345559467247485861466770385364317348726931687832497850687a346e516f384b64796f4d6c7945597a6146453250317a4a4351386a73676544714a2f46384a336d6d623774567430596c4a64487770526d4465487470417a5870537864514671436e5668426e446236625a7948356e355a5a566e655536334738364b2b386968722f47514738685549703570516b6f574e2f536f3070616952696b5179557970304d733461492b6978543344487370444c5330736f347a6a475645425a654241526d77484c627979775142724a636c7330576d656754726541674335637a6c4f663179674c545350554a444968452f71464c55304b384952326367706d374768394448566d56736f32556d39513037576e676a337a79704855424879504641587a4f5a71496f5249555351304b4559516757516f6875423936536b624a41566f3158644f4569712f784b46555059567255474754394151717264456b76684d6f4c6e734c6e584e6a4f4c46435473435566597643414b416f75394c4a6142616468784b766e433064764a716d7863417268414c5278426b595758457053384c7859435169424f67495330344b674c6d6b6148366d415935496e69643539742b594b614f6b3852426f4e476d334b746c66456933672b526a6f68794b62365975794471424e427a55614a5157446e432f306730726f5a637a7333793636755a6449347449416c525741347775666f4c425231764b71363155526f452b5253515a57576a7a7970566a614d6b6550584b444f344b6f776f366567625a524f71417852505a737a50664d51726d7a4a61444c55574535446172674e3834503745456d6f67496c526579516e6f536c62534b617a4b4f49497a5546586e744b454b4975646774424c4e704b7933696145497a6b396e4651686b2b5a6a52462f435938316678444e77536a77377241644a6f4261456f484b3851626d68594a684237737a3048765a546c4652786169472b7046534835556573317a635777304d6442377744754c487749725654486f45344b344473354c7a4a584646475a54443343434a59676754716e4e483431772f4b7342713752493538654f65594144356432444869574c33684863456770504e2b7462524763784450564f614842712f717357424942305537314e44326f6a653138437a626b354f766f4545514e365263316d3077457363774e55436b4162707054654634644748754c38594b526770656f564f36422f6a6331683246425357426961644275657472435130677952796e4e4259526c376b4a55554c6f7450335655425756374b755a7a34413861524d4b546e5a6d2f586439504a396977376c336e625253315a4774713230484f49714d3532467649526f51754e6b3463436b3244545a554f6e7544305764794d4356697838656c4e496f4d3932757969456c63456b726e6d377162794578555a556b2b77325359576c4e746a537967327838436d324153615752533132475735325542696232714f6c684249595153344558705355596e6c2f72794b36367277476b716c644f343258474241766f72567459534f344e3563654b496f594c4d696c367661677856537652454c2f73563270625941642b6f4476302f6e5a73556735775563366d48466764494a3868415269574d3246304b61516f556237634555396851466872736f2f43696569486f6e72584d303455374851496178514e7a774b476c68676d4e414a7471716a417450424f71454d5978556a6d6e57744c3159646c755559516d6c43474e6a483072773962364a434b2b7432786f5162736865776e5a55744b6141587550626a72546d584463555a346e41593432553873534459544e33742f756b71442f58496c74646c6339466d712f334c644c594267386a57325433744b35434b4a69686d55465531716d6f4d325235374a6c626c30326174586b6c6c4e5559414136656d36556b4d362f6e4f5661487773316d6564346b764265485954316c45435237784771345765566a535261466a754d45436251677159786d70795a6b4b747155675836727a6e564c767832316f7444304638464c796a596639555a6174524f7a687557416a39554146416175726b50524e747659533958424138453865627972354257706d70464f30424c6d69663439366b4f37624f6d47367934495a4f716362354b6c6f613831596b4174587252337937757370455232687947673673694f33797a6f77514943527751353549597143397a3167716f494f5967513072706a656e454d4d7964647644537a6a755a4a7330724a2b6b5167693978414356535969626d656f70666f664939746d62715153725848756f5630504339543175564d4d516e4f476e4a64786c41494d4b5152747569627265427070447435612f4365746b2f534f314f497542512b554279706c51713276397669754157372f747763457072513337773869756441475541365732356e79694f41614d645a366b4d5431774e6b632f4b43343246777a43497847344f564f3468302f446f6768634e313572696d446f514441524c45425a366e6f486e56676668744e33644771705475696167663878717347576764384937694651684d427a6a47354b2f39504d4342712b7439382f6366384646435173756675423958387a3054436f55647538576b6e754346677a72346a7173575657516a7236745463757a32792f2b357133456a6878716b5342685a6b306254614e3476346b39656a484e303745714a684950765774702f32763676726c4e62737a4754637367776534332f334c72484d562b2b2b71565837582f612f3262375152736648373435632f534d4f6a382f747a70394f727a5a6352322f684d645737776b386e6a514c4e6c39664c4b75494b514a5076317750627072686e702b6230764230654e6d5a64546b38506233657a7462666e5a362b32394b47496e71666d306a344446387644346877636746543849767a35757a30644c6462342f6b4d73553750686c3836347939507a3971414d4f6c646936663432646e5a6c57416e3638335a326274447565766b4c2b6941762f71625578693841377947523967687648767542355432416457764d45316f2f424b6d667a356754365542564b527a4242687541666b376741494a34632f587a6a365041307268734f6c57727358307778494b6c594a4570322b2b624731344d6b4c5a54416c322f4f616b50523441523063415964377050336945523145504d6e38624d4f784d504c7343306237412b70304566446b364b71454651434d41636c476a42354e514737436c6767336735664273314e454d7642683241643932317232472f6e4f7830654868484e5143484c595338516251484936473139327747413362586a6f616451477848397a736e2b466f644c67304f666c724e477741523375414977513865547361646c537a447a6a73416972577851676d5771505273443663764c65417346426270594f52414c7a385042792b365155456c513648585543706867484f35546363645a4339476b4246414c596c58414e2b67666544397333554869444d3367504541654831743948686f4e67434b732b48415a2f4434556872543330464550783041455a4d766f314750655551417236786f756774614c316c5a584d4e57434c416c355a7958675530726f624469355042384f74687641625130595a6f3552616c624143726b7776776d7462345142747062634442384f31652b523343694f543532376c307544574173487a582f5465416f54556544713561485945323048592f34756a6a7757425051736b5a444c546e685051567443642f44515a6a52394d47304671324d7545524152336c737a6277576c346a414c63624f41536f587730474638355833704e685432444b57502f7935545073712b3443347630386c36344249646831584c3471496337346c6a67393578774641532f575579396167466f4443446d66447251575356322b4b7145596374316236514b67686b426d48364144674e4865616c304a3932585259613766653237635347694f4e61304e364347674969574a4b512b3063696645433556714c77417438496a443139685349356c32434642754a4e5249634b567072534e6b6f476d76417372616f502f5132512b6f43554161786c547a646e6544416446494731445458746751414c556a67424e74432b6931415455425346695a554533626364424c77497344456d72392f77477141376a7a4c484d394b556b632f434670393139453967466c4d745a6a614e4675376d7541736a5a6541354979324f775741655653614e4c305a46707570516a7765726b4c364e68346f3746547245586b3434414541546e2b6173593278333246343732744c616a4838676c4a6577454a47627455396d537641796a334137707251476e426d6d7668426c434b6d4f77316c305a4741524a733577633273376658765a492b5933622b516b4a343767384c39784e4d77533971525a6d3975394177596e6755706c4f634d41793363617a44512b74734155384a666f54687a6f5947767579394e75415a39445a6654566971355848773246774a39303374613072727336667a5545632f7a7038542b702f32702f312f742f38412b724a44466c344139755541414141415355564f524b35435949495c783364273b76617220693d5b2764696d675f3137275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220733d27646174613a696d6167652f706e673b6261736536342c6956424f5277304b47676f414141414e5355684555674141414841414141427743414d414141447850675235414141416231424d5645575a4141442f2f2f2b574141435a45684c7a354f535341414474323976713164585772713743686f61765656586e304e432f666e372b2b2f763538764b705330754e414143694f546e66774d446275626d5a4367717153456a323675713965586e52704b53474141446b794d6969516b4b36635847684e44544a6c4a537457566d644b69716149534764485232775947433161576b305454574c4141414376306c455156526f6765325759584f6a4942434764654e714c4e56456a4d6247746b6d622f502f665747424230574454752f4e6d626d37322f59514c374f4d43757842464c42614c78574b7857437757693856697356697366307962425230696d4a73414944544a39335a59374848434e416d7150554c547a6d785a2f626f787944642f5576734f6f7a63784759386859426d486c654d755945324f326b6c5654477744454d36547765304b77446a756c5074714f756e7369466a2f45664170334b465763427068584476484d4c5758693843796c764a6b42376c6d6a7057553076374f53526c623636626f34564c4c4f7445687044546e772f4a793353316f5746624c4c53774278517675477a744f4e63316135684168496e6b734b745838644f76566f65725a622f562f584d6c794a4d2b5971625a384a64767a486b4d42456a446252324158554a30736175626143566a677a767549552f32424769695177685947434f5a487a3065374a6148776667324975563331616744754f7a4a56777a2b30384c7761454b357545776367374f6959644b5a667539726962774450433043374f3855594964684e31716b495a6933376878466d716954495759537471545233533270505462735a675743585761556943724f2f6a34415267436d524870427331476d4250514b364c5978504f414b6a4379574f536b57546e506c6a6f4f5036774e46716765393533746b7a47736533385a53717543516470452f5535366634694659425469534e4a77654547316d504a6b4d6b7267394d71617734594252524b6c494e754d4c6177474a3767436b514b52564e5257763159566746574769565a646f3177385867674e434d4662746243316a6365715871416a6834475a63557831586f56774d3264374e486f4574464e64643872674c6366514e307165694b30393848326c5455536668546f436f6a376a355556364f37783754563359657a797733785252394f6f63786a4b756f455656507339625230487872506653704552754e4b49635357526a6171535365777949536f76542b2b6e49526f615843717468636f46585553536945536370514963566f737037506e556f6f42612b4c4e39743830543242547354334d58306a46556f687a59425943706a3751383675416b626b5664524a4f6763464831426f52556970576438446c434a736b3955585076706c56657341334d5a695478747939535772324749552f4a666a794a754a4d49665053684f453734476d4a78324b78574377576938566973566773466f7646597630332b67492b6453615a504562466d6741414141424a52553545726b4a6767675c7833645c783364273b76617220693d5b2764696d675f3139275d3b5f736574496d6167657353726328692c73293b7d2928293b2866756e6374696f6e28297b76617220653d27704d524f586250624472505339414f426d4b50594251273b2866756e6374696f6e28297b76617220613d652c623d77696e646f772e706572666f726d616e6365262677696e646f772e706572666f726d616e63652e6e617669676174696f6e3b622626323d3d622e74797065262677696e646f772e70696e6728222f67656e5f3230343f63743d6261636b627574746f6e2665693d222b61293b7d292e63616c6c2874686973293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363743d7b7d3b676f6f676c652e637363742e70733d27414f765661773144704144365477704f5055674e344e6f72467065555c7832367573745c78336431353635353239363336323834373838273b7d2928293b7d2928293b2866756e6374696f6e28297b2866756e6374696f6e28297b676f6f676c652e637363742e72643d747275653b7d2928293b7d2928293b2866756e6374696f6e28297b77696e646f772e78703d66756e6374696f6e2861297b66756e6374696f6e206528682c662c67297b72657475726e227870222b282278223d3d663f2263223a227822292b677d666f722876617220623d2f5c62787028787c6329285c643f295c622f3b613b297b76617220633d612e636c6173734e616d652c643d632e6d617463682862293b69662864297b612e636c6173734e616d653d632e7265706c61636528622c65293b6966282263223d3d645b315d29666f7228613d612e676574456c656d656e747342795461674e616d652822696d6722292c623d303b623c612e6c656e6774683b2b2b6229696628633d615b625d2c643d632e6765744174747269627574652822646174612d6c6c222929632e7372633d642c632e72656d6f76654174747269627574652822646174612d6c6c22293b627265616b7d613d612e706172656e74456c656d656e747d7d3b7d2928293b676f6f676c652e647274792626676f6f676c652e6472747928293b3c2f7363726970743e3c2f626f64793e3c2f68746d6c3e", - "rawHeaders": [ - "Content-Type", - "text/html; charset=ISO-8859-1", - "Date", - "Sat, 10 Aug 2019 01:21:31 GMT", - "Expires", - "-1", - "Cache-Control", - "private, max-age=0", - "P3P", - "CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"", - "Server", - "gws", - "X-XSS-Protection", - "0", - "X-Frame-Options", - "SAMEORIGIN", - "Set-Cookie", - "1P_JAR=2019-08-10-01; expires=Mon, 09-Sep-2019 01:21:31 GMT; path=/; domain=.google.com", - "Set-Cookie", - "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/complete/search; domain=.google.com; HttpOnly", - "Set-Cookie", - "CGIC=IiFhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L3BsYWluLCAqLyo; expires=Thu, 06-Feb-2020 01:21:31 GMT; path=/search; domain=.google.com; HttpOnly", - "Set-Cookie", - "NID=188=vTMutucOBO-Yl5bpVtVnzkN1voOukQ24RkD0wuuzeNL_BDPMEB90MqBF06HFaILh_fs-PO8JGLhIjkSb3nxl9Rzf8L7CxJtk_yJF0aEgi2znY0rMT_dQr6_5tYfVNKU9u0d2BoXOVOWHEN3ZzaD7q6yRUb44yH3vjL0kue6Ki0s; expires=Sun, 09-Feb-2020 01:21:31 GMT; path=/; domain=.google.com; HttpOnly", - "Accept-Ranges", - "none", - "Vary", - "Accept-Encoding", - "Connection", - "close" - ], - "responseIsBinary": true - } -] diff --git a/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem b/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem deleted file mode 100644 index e2b79024da..0000000000 --- a/packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBqzCCARQCCQDLcUeJsLDL5jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJD -QTELMAkGA1UECAwCUUMwHhcNMTkwOTI5MjIwMDI2WhcNMTkxMDI5MjIwMDI2WjAa -MQswCQYDVQQGEwJDQTELMAkGA1UECAwCUUMwgZ8wDQYJKoZIhvcNAQEBBQADgY0A -MIGJAoGBALhfi1dwIyC1Jha4N/j/VtlPPi+j+SZQGZqLNVVgzzGY7+cc3VkCySZD -yXh3Z+/ftp9DDKdHRutJQE0R4peSDussC/IQDJKzuKN/O9S6tnNlgUr5YZLRENxL -FSJIY5cIkty50IrEhlN5QeDJP8p4yrYq9J6M0yzyfdqIWI3CBqbzAgMBAAEwDQYJ -KoZIhvcNAQEFBQADgYEArnOeXmXXJTK39Ma25elHxlYUZiYOBu/truy5zmx4umyS -GyehAv+jRIanoCRWtOBnrjS5CY/6cC64aIVLMoqXEFIL7q/GD0wEM/DS8rN7KTcp -w+nIX98srYaAFeQZScPioS6WpXz5AjbTVhvAwkIm2/s6dOlX31+1zu6Zu6ASSuQ= ------END CERTIFICATE----- diff --git a/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem b/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem deleted file mode 100644 index 405c5fa0d7..0000000000 --- a/packages/opentelemetry-plugin-https/test/fixtures/server-key.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQC4X4tXcCMgtSYWuDf4/1bZTz4vo/kmUBmaizVVYM8xmO/nHN1Z -AskmQ8l4d2fv37afQwynR0brSUBNEeKXkg7rLAvyEAySs7ijfzvUurZzZYFK+WGS -0RDcSxUiSGOXCJLcudCKxIZTeUHgyT/KeMq2KvSejNMs8n3aiFiNwgam8wIDAQAB -AoGBAKBztcYQduGeBFm9VCjDvgc8KTg4kTlAeCfAglec+nOFTzJoMlGmVPuR/qFx -+OgOXtXW+goRw6w7gVQQ/os9tvCCp7awSC5UCfPejHh6bW2B0BF2lZJ6B9y+u5Fa -/p8oKoJGcC4eagVnDojuoYJHSqWBf7d7V/U54NpxwgBTsHAhAkEA8PJROgWzjMl2 -Gs5j8oBldEqzrC/d4K1uMEvCTb4RJ+t6jWq+Ug/vqvCfIcLfxHbOmTbOHTfhpv/d -NUf9eDyBGwJBAMPkZaHP5vPDd900MqypLVasollzxgPnMUg35EEQJLAbb/5xG3X9 -ZbaVDTRtLQYNFvDZLlTpRpCPxZCgrn9hJwkCQQDPEVChLrkpqxFm5CydAZ8vG+vh -dJmYNzPVKaZorYmM5yBBXJUHbU6pd3UqzJEGBJx0q9bi4V156bYvzhiVNlo1AkBu -1hbvFCwPtoRmg3c8nEhL50fApzHd2XzX6M/cRF8Nyah3ZdXsz6AyS2l6RV+ZMeTO -B4QghRDpEH/vUgsJhZXJAkB5GQZPJh6/kozc5+Ffc60ThN/58SX0KEFeKnWRlzfr -vfBXwcmaz1oNXN+kcWdLnKbr/tx+3UQ6weRRmeYX/hOi ------END RSA PRIVATE KEY----- diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts deleted file mode 100644 index 88140d73ab..0000000000 --- a/packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Http } from '@opentelemetry/plugin-http'; -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as https from 'https'; -import { AddressInfo } from 'net'; -import * as nock from 'nock'; -import * as sinon from 'sinon'; -import { plugin } from '../../src/https'; -import { httpsRequest } from '../utils/httpsRequest'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import * as api from '@opentelemetry/api'; - -describe('HttpsPlugin', () => { - let server: https.Server; - let serverPort = 0; - - describe('disable()', () => { - const provider = new NodeTracerProvider(); - // const tracer = provider.getTracer('test-https') - let tracer: api.Tracer; - before(() => { - nock.cleanAll(); - nock.enableNetConnect(); - - plugin.enable((https as unknown) as Http, provider); - tracer = plugin['_tracer']; - // Ensure that https module is patched. - assert.strictEqual(https.Server.prototype.emit.__wrapped, true); - server = https.createServer( - { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), - }, - (request, response) => { - response.end('Test Server Response'); - } - ); - - server.listen(serverPort); - server.once('listening', () => { - serverPort = (server.address() as AddressInfo).port; - }); - }); - - beforeEach(() => { - tracer.startSpan = sinon.spy(); - }); - - afterEach(() => { - sinon.restore(); - }); - - after(() => { - server.close(); - }); - describe('unpatch()', () => { - it('should not call tracer methods for creating span', async () => { - plugin.disable(); - const testPath = '/incoming/unpatch/'; - - const options = { host: 'localhost', path: testPath, port: serverPort }; - - await httpsRequest.get(options).then(result => { - assert.strictEqual( - (tracer.startSpan as sinon.SinonSpy).called, - false - ); - - assert.strictEqual(https.Server.prototype.emit.__wrapped, undefined); - }); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts deleted file mode 100644 index a4ad62f09e..0000000000 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - SpanStatusCode, - context, - propagation, - Span as ISpan, - SpanKind, - setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ContextManager } from '@opentelemetry/api'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { - GeneralAttribute, - HttpAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as https from 'https'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpsPlugin, plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; - -const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; - -let server: https.Server; -const serverPort = 32345; -const protocol = 'https'; -const hostname = 'localhost'; -const serverName = 'my.server.name'; -const pathname = '/test'; -const memoryExporter = new InMemorySpanExporter(); -const provider = new NodeTracerProvider(); -const tracer = provider.getTracer('test-https'); -provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); -propagation.setGlobalPropagator(new DummyPropagation()); - -function doNock( - hostname: string, - path: string, - httpCode: number, - respBody: string, - times?: number -) { - const i = times || 1; - nock(`${protocol}://${hostname}`) - .get(path) - .times(i) - .reply(httpCode, respBody); -} - -export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpsPlugin', () => { - let contextManager: ContextManager; - - beforeEach(() => { - contextManager = new AsyncHooksContextManager().enable(); - context.setGlobalContextManager(contextManager); - }); - - afterEach(() => { - contextManager.disable(); - context.disable(); - }); - - it('should return a plugin', () => { - assert.ok(plugin instanceof HttpsPlugin); - }); - - it('should match version', () => { - assert.strictEqual(process.versions.node, plugin.version); - }); - - it(`moduleName should be ${protocol}`, () => { - assert.strictEqual(protocol, plugin.moduleName); - }); - - describe('enable()', () => { - describe('with bad plugin options', () => { - let pluginWithBadOptions: HttpsPlugin; - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); - }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); - }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); - }, - }; - pluginWithBadOptions = new HttpsPlugin(process.versions.node); - pluginWithBadOptions.enable( - (https as unknown) as Http, - provider, - config - ); - server = https.createServer( - { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), - }, - (request, response) => { - response.end('Test Server Response'); - } - ); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - pluginWithBadOptions.disable(); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assertSpan(incomingSpan, SpanKind.SERVER, validations); - assertSpan(outgoingSpan, SpanKind.CLIENT, validations); - assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], - serverPort - ); - }); - }); - describe('with good plugin options', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - '/ignored/string', - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - serverName, - }; - plugin.enable((https as unknown) as Http, provider, config); - server = https.createServer( - { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), - }, - (request, response) => { - if (request.url?.includes('/ignored')) { - tracer.startSpan('some-span').end(); - } - response.end('Test Server Response'); - } - ); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - plugin.disable(); - }); - - it(`${protocol} module should be patched`, () => { - assert.strictEqual(https.Server.prototype.emit.__wrapped, true); - }); - - it(`should not patch if it's not a ${protocol} module`, () => { - const httpsNotPatched = new HttpsPlugin(process.versions.node).enable( - {} as Http, - provider, - {} - ); - assert.strictEqual(Object.keys(httpsNotPatched).length, 0); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}`, - { - headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', - }, - } - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - serverName, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], - '' - ); - assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], - serverPort - ); - - [ - { span: incomingSpan, kind: SpanKind.SERVER }, - { span: outgoingSpan, kind: SpanKind.CLIENT }, - ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); - assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP - ); - assertSpan(span, kind, validations); - }); - }); - - const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505]; - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; - - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - - const isReset = memoryExporter.getFinishedSpans().length === 0; - assert.ok(isReset); - - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - const reqSpan = spans[0]; - - assert.strictEqual(result.data, httpErrorCodes[i].toString()); - assert.strictEqual(spans.length, 1); - - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assertSpan(reqSpan, SpanKind.CLIENT, validations); - }); - } - - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - } - - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; - const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - await context.with(setSpan(context.active(), span), async () => { - for (let i = 0; i < num; i++) { - await httpsRequest.get(`${protocol}://${hostname}${testPath}`); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); - assert.strictEqual( - span.context().traceId, - spans[i].spanContext.traceId - ); - } - span.end(); - const spans = memoryExporter.getFinishedSpans(); - // 5 child spans ended + 1 span (root) - assert.strictEqual(spans.length, 6); - }); - }); - - for (const ignored of ['string', 'function', 'regexp']) { - it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { - const testPath = `/ignored/${ignored}`; - - await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - } - - for (const arg of ['string', {}, new Date()]) { - it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpsRequest.get(arg); - } catch (error) { - // request has been made - // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); - } - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - }); - } - - for (const arg of [true, 1, false, 0, '']) { - it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpsRequest.get(arg as any); - } catch (error) { - // request has been made - // nock throw - assert.ok( - error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') - ) > 0 - ); - } - const spans = memoryExporter.getFinishedSpans(); - // for this arg with don't provide trace. We pass arg to original method (https.get) - assert.strictEqual(spans.length, 0); - }); - } - - it('should have 1 ended span when request throw on bad "options" object', () => { - try { - https.request({ protocol: 'telnet' }); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request throw on bad "options" object', () => { - nock.cleanAll(); - nock.enableNetConnect(); - try { - https.request({ protocol: 'telnet' }); - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request is aborted', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .socketDelay(50) - .reply(200, ''); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - req.setTimeout(10, () => { - req.abort(); - reject('timeout'); - }); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length >= 6); - } - }); - - it('should have 1 ended span when request is aborted after receiving response', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .delay({ - body: 50, - }) - .replyWithFile(200, `${process.cwd()}/package.json`); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - req.abort(); - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length > 7); - } - }); - - it("should have 1 ended span when response is listened by using req.on('response')", done => { - const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); - const req = https.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], - 404 - ); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - done(); - }); - }); - req.end(); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts deleted file mode 100644 index e742977e86..0000000000 --- a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { context, Span, SpanKind } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { Http } from '@opentelemetry/plugin-http'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import * as assert from 'assert'; -import axios, { AxiosResponse } from 'axios'; -import * as got from 'got'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as https from 'https'; -import * as nock from 'nock'; -import * as path from 'path'; -import * as request from 'request-promise-native'; -import * as superagent from 'superagent'; -import * as url from 'url'; -import { plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { Socket } from 'net'; - -const memoryExporter = new InMemorySpanExporter(); - -export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('Packages', () => { - let mockServerPort = 0; - let mockServer: https.Server; - const sockets: Array = []; - before(done => { - mockServer = https.createServer( - { - key: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-key.pem') - ), - cert: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-cert.pem') - ), - }, - (req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - } - ); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - memoryExporter.reset(); - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - describe('get', () => { - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - plugin.enable((https as unknown) as Http, provider); - }); - - after(() => { - // back to normal - nock.cleanAll(); - nock.enableNetConnect(); - }); - - let resHeaders: http.IncomingHttpHeaders; - [ - { name: 'axios', httpPackage: axios }, //keep first - { name: 'superagent', httpPackage: superagent }, - { name: 'got', httpPackage: { get: (url: string) => got(url) } }, - { - name: 'request', - httpPackage: { get: (url: string) => request(url) }, - }, - ].forEach(({ name, httpPackage }) => { - it(`should create a span for GET requests and add propagation headers by using ${name} package`, async () => { - if (process.versions.node.startsWith('12') && name === 'got') { - // got complains with nock and node version 12+ - // > RequestError: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type function - // so let's make a real call - nock.cleanAll(); - nock.enableNetConnect(); - } else { - nock.load(path.join(__dirname, '../', '/fixtures/google.json')); - } - - const urlparsed = url.parse( - `https://localhost:${mockServerPort}/?query=test` - ); - const result = await httpPackage.get(urlparsed.href!); - if (!resHeaders) { - const res = result as AxiosResponse<{}>; - resHeaders = res.headers; - } - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: urlparsed.hostname!, - httpStatusCode: 200, - httpMethod: 'GET', - pathname: urlparsed.pathname!, - path: urlparsed.path, - resHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - - switch (name) { - case 'axios': - assert.ok( - result.request._headers[DummyPropagation.TRACE_CONTEXT_KEY] - ); - assert.ok( - result.request._headers[DummyPropagation.SPAN_CONTEXT_KEY] - ); - break; - case 'got': - case 'superagent': - break; - default: - break; - } - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts deleted file mode 100644 index 74a247e909..0000000000 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; -import { SpanKind, Span, context } from '@opentelemetry/api'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as https from 'https'; -import { plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; -import * as url from 'url'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; - -const protocol = 'https'; -const serverPort = 42345; -const hostname = 'localhost'; -const memoryExporter = new InMemorySpanExporter(); - -export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpsPlugin Integration tests', () => { - let mockServerPort = 0; - let mockServer: https.Server; - const sockets: Array = []; - before(done => { - mockServer = https.createServer( - { - key: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-key.pem') - ), - cert: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-cert.pem') - ), - }, - (req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - } - ); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - memoryExporter.reset(); - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - - describe('enable()', () => { - before(function (done) { - // mandatory - if (process.env.CI) { - done(); - return; - } - - utils.checkInternet(isConnected => { - if (!isConnected) { - this.skip(); - // don't disturb people - } - done(); - }); - }); - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const ignoreConfig = [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, - }; - try { - plugin.disable(); - } catch (e) {} - plugin.enable((https as unknown) as Http, provider, config); - }); - - after(() => { - plugin.disable(); - }); - - it('should create a rootSpan for GET requests and add propagation headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - `${protocol}://localhost:${mockServerPort}/?query=test` - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`) - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), - { - headers: { 'x-foo': 'foo' }, - } - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); - assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP - ); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('custom attributes should show up on client spans', async () => { - const result = await httpsRequest.get( - `${protocol}://localhost:${mockServerPort}/` - ); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = Object.assign( - { headers: { Expect: '100-continue' } }, - url.parse(`${protocol}://localhost:${mockServerPort}/`) - ); - - const result = await httpsRequest.get(options); - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, - ]) { - it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( - headers - )}`, done => { - let validations: { - hostname: string; - httpStatusCode: number; - httpMethod: string; - pathname: string; - reqHeaders: http.OutgoingHttpHeaders; - resHeaders: http.IncomingHttpHeaders; - }; - let data = ''; - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = { headers }; - const req = https.get( - `${protocol}://localhost:${mockServerPort}/`, - options, - (resp: http.IncomingMessage) => { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - validations = { - hostname: 'localhost', - httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', - resHeaders: resp.headers, - /* tslint:disable:no-any */ - reqHeaders: (res.req as any).getHeaders - ? (res.req as any).getHeaders() - : (res.req as any)._headers, - /* tslint:enable:no-any */ - }; - }); - } - ); - - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.ok(data); - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - done(); - }); - }); - } - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts b/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts deleted file mode 100644 index 4994bf3c94..0000000000 --- a/packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - Context, - TextMapPropagator, - TraceFlags, - setSpanContext, - getSpanContext, -} from '@opentelemetry/api'; -import * as http from 'http'; - -export class DummyPropagation implements TextMapPropagator { - static TRACE_CONTEXT_KEY = 'x-dummy-trace-id'; - static SPAN_CONTEXT_KEY = 'x-dummy-span-id'; - extract(context: Context, carrier: http.OutgoingHttpHeaders) { - const extractedSpanContext = { - traceId: carrier[DummyPropagation.TRACE_CONTEXT_KEY] as string, - spanId: DummyPropagation.SPAN_CONTEXT_KEY, - traceFlags: TraceFlags.SAMPLED, - isRemote: true, - }; - if (extractedSpanContext.traceId && extractedSpanContext.spanId) { - return setSpanContext(context, extractedSpanContext); - } - return context; - } - inject(context: Context, headers: { [custom: string]: string }): void { - const spanContext = getSpanContext(context); - if (!spanContext) return; - headers[DummyPropagation.TRACE_CONTEXT_KEY] = spanContext.traceId; - headers[DummyPropagation.SPAN_CONTEXT_KEY] = spanContext.spanId; - } - fields(): string[] { - return [ - DummyPropagation.TRACE_CONTEXT_KEY, - DummyPropagation.SPAN_CONTEXT_KEY, - ]; - } -} diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts deleted file mode 100644 index 5921f47eb6..0000000000 --- a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { SpanKind } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { DummyPropagation } from './DummyPropagation'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { parseResponseStatus } from '@opentelemetry/plugin-http'; - -export const assertSpan = ( - span: ReadableSpan, - kind: SpanKind, - validations: { - httpStatusCode: number; - httpMethod: string; - resHeaders: http.IncomingHttpHeaders; - hostname: string; - pathname: string; - reqHeaders?: http.OutgoingHttpHeaders; - path?: string | null; - serverName?: string; - component: string; - } -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], - span.status.message - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], - validations.httpMethod - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], - validations.path || validations.pathname - ); - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], - validations.httpStatusCode - ); - assert.ok(span.endTime); - assert.strictEqual(span.links.length, 0); - assert.strictEqual(span.events.length, 0); - assert.deepStrictEqual( - span.status, - parseResponseStatus(validations.httpStatusCode) - ); - - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); - - if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; - if (userAgent) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], - userAgent - ); - } - } - if (span.kind === SpanKind.CLIENT) { - assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], - validations.hostname, - 'must be consistent (PEER_NAME and hostname)' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], - 'must have PEER_IP' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], - 'must have PEER_PORT' - ); - assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string - ) > -1, - 'must be consistent' - ); - } - if (span.kind === SpanKind.SERVER) { - if (validations.serverName) { - assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], - validations.serverName, - ' must have serverName attribute' - ); - } - assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], - 'must have HOST_PORT' - ); - assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], - 'must have HOST_IP' - ); - assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); - } else if (validations.reqHeaders) { - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - } -}; diff --git a/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts b/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts deleted file mode 100644 index f75cf1e566..0000000000 --- a/packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as http from 'http'; -import * as https from 'https'; -import { URL } from 'url'; - -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - -type GetResult = Promise<{ - data: string; - statusCode: number | undefined; - resHeaders: http.IncomingHttpHeaders; - reqHeaders: http.OutgoingHttpHeaders; - method: string | undefined; -}>; - -function get(input: string | URL, options?: https.RequestOptions): GetResult; -function get(input: https.RequestOptions): GetResult; -function get(input: any, options?: any): GetResult { - return new Promise((resolve, reject) => { - // eslint-disable-next-line prefer-const - let req: http.ClientRequest; - - function onGetResponseCb(resp: http.IncomingMessage): void { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve({ - data, - statusCode: res.statusCode, - reqHeaders: req.getHeaders ? req.getHeaders() : (req as any)._headers, - resHeaders: res.headers, - method: res.req.method, - }); - }); - resp.on('error', err => { - reject(err); - }); - } - req = - options != null - ? https.get(input, options, onGetResponseCb) - : https.get(input, onGetResponseCb); - req.on('error', err => { - reject(err); - }); - return req; - }); -} - -export const httpsRequest = { - get, -}; diff --git a/packages/opentelemetry-plugin-https/test/utils/utils.ts b/packages/opentelemetry-plugin-https/test/utils/utils.ts deleted file mode 100644 index a4e37eabea..0000000000 --- a/packages/opentelemetry-plugin-https/test/utils/utils.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as dns from 'dns'; - -export const checkInternet = (cb: (isConnected: boolean) => void) => { - dns.lookup('google.com', err => { - if (err && err.code === 'ENOTFOUND') { - cb(false); - } else { - cb(true); - } - }); -}; diff --git a/packages/opentelemetry-plugin-https/tsconfig.json b/packages/opentelemetry-plugin-https/tsconfig.json deleted file mode 100644 index 3308398df0..0000000000 --- a/packages/opentelemetry-plugin-https/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": ".", - "outDir": "build" - }, - "include": [ - "src/**/*.ts", - "test/**/*.ts" - ], - "references": [ - { - "path": "../opentelemetry-context-async-hooks" - }, - { - "path": "../opentelemetry-core" - }, - { - "path": "../opentelemetry-node" - }, - { - "path": "../opentelemetry-plugin-http" - }, - { - "path": "../opentelemetry-semantic-conventions" - }, - { - "path": "../opentelemetry-tracing" - } - ] -} diff --git a/packages/opentelemetry-propagator-b3/README.md b/packages/opentelemetry-propagator-b3/README.md index 5748ebc74c..26ad1c4bba 100644 --- a/packages/opentelemetry-propagator-b3/README.md +++ b/packages/opentelemetry-propagator-b3/README.md @@ -65,17 +65,15 @@ X-B3-Sampled: {SamplingState} - Optional - Debug is encoded as `X-B3-Flags`: 1. Absent or any other value can be ignored. Debug implies an accept decision, so don't also send the `X-B3-Sampled` header. -## Propagator Implementations - -### B3Propagator +## B3 Propagation The default `B3Propagator` implements b3 propagation according to the [OpenTelemetry specification][otel-b3-requirements]. It extracts b3 context from multi and single header encodings and injects context using the -single-header b3 encoding. The inject encoding can be changed to multi-header -via configuration. +single-header b3 encoding by default. The inject encoding can be changed to +multi-header via configuration. See the examples below. -Example usage (default): +### B3 Single-Header Configuration ```javascript const api = require('@opentelemetry/api'); @@ -84,7 +82,7 @@ const { B3Propagator } = require('@opentelemetry/propagator-b3'); api.propagation.setGlobalPropagator(new B3Propagator()); ``` -Example usage (specify inject encoding): +### B3 Multi-Header Configuration ```javascript const api = require('@opentelemetry/api'); @@ -95,53 +93,21 @@ api.propagation.setGlobalPropagator( ); ``` -### B3SinglePropagator - -If a distributed system only needs support for the b3 single-header -encoding it can use the `B3SinglePropagator` directly. - -Example usage: - -```javascript -const api = require('@opentelemetry/api'); -const { B3SinglePropagator } = require('@opentelemetry/propagator-b3'); - -api.propagation.setGlobalPropagator(new B3SinglePropagator()); -``` - -### B3MultiPropagator - -If a distributed system only needs support for the b3 multi-header -encoding it can use the `B3MultiPropagator` directly. - -Example usage: - -```javascript -const api = require('@opentelemetry/api'); -const { B3MultiPropagator } = require('@opentelemetry/propagator-b3'); - -api.propagation.setGlobalPropagator(new B3MultiPropagator()); -``` - -### CompositePropagator - -If a distributed system needs to support both single and multiple header -encodings for inject and extract the `B3SinglePropagator` and -`B3MultiPropagator` can be used in conjunction with a `CompositePropagator`. +### B3 Single and Multi-Header Configuration -Example usage: +The B3Propagator always extracts both the single and multi-header b3 encodings. +If you need to inject both encodings this can accomplished using a composite +propagator. ```javascript const api = require('@opentelemetry/api'); -const { CompositePropagator } = require('@opentelemetry/core'); -const { - B3SinglePropagator, - B3MultiPropagator, -} = require('@opentelemetry/propagator-b3'); - +const { B3Propagator } = require('@opentelemetry/propagator-b3'); api.propagation.setGlobalPropagator( new CompositePropagator({ - propagators: [new B3SinglePropagator(), new B3MultiPropagator()], + propagators: [ + new B3Propagator(), + new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }), + ], }) ); ``` diff --git a/packages/opentelemetry-propagator-b3/package.json b/packages/opentelemetry-propagator-b3/package.json index 0cd2ae04e4..feecb85aff 100644 --- a/packages/opentelemetry-propagator-b3/package.json +++ b/packages/opentelemetry-propagator-b3/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/propagator-b3", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry B3 propagator provides context propagation for systems that are using the B3 header format", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -38,20 +38,21 @@ "publishConfig": { "access": "public" }, - "dependencies": { + "peerDependencies": { "@opentelemetry/api": "^1.0.0-rc.0" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "codecov": "3.8.1", "gts": "3.1.0", "istanbul-instrumenter-loader": "3.0.1", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "ts-loader": "8.0.18", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", - "typescript": "4.2.3" + "typescript": "4.2.4" } } diff --git a/packages/opentelemetry-propagator-b3/src/B3MultiPropagator.ts b/packages/opentelemetry-propagator-b3/src/B3MultiPropagator.ts index 7f0e414c05..dba3a70de3 100644 --- a/packages/opentelemetry-propagator-b3/src/B3MultiPropagator.ts +++ b/packages/opentelemetry-propagator-b3/src/B3MultiPropagator.ts @@ -17,6 +17,7 @@ import { Context, getSpanContext, + isInstrumentationSuppressed, isSpanContextValid, isValidSpanId, isValidTraceId, @@ -26,15 +27,15 @@ import { TextMapSetter, TraceFlags, } from '@opentelemetry/api'; +import { + X_B3_TRACE_ID, + X_B3_SPAN_ID, + X_B3_SAMPLED, + X_B3_PARENT_SPAN_ID, + X_B3_FLAGS, +} from './constants'; import { B3_DEBUG_FLAG_KEY } from './common'; -/* b3 multi-header keys */ -export const X_B3_TRACE_ID = 'x-b3-traceid'; -export const X_B3_SPAN_ID = 'x-b3-spanid'; -export const X_B3_SAMPLED = 'x-b3-sampled'; -export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid'; -export const X_B3_FLAGS = 'x-b3-flags'; - const VALID_SAMPLED_VALUES = new Set([true, 'true', 'True', '1', 1]); const VALID_UNSAMPLED_VALUES = new Set([false, 'false', 'False', '0', 0]); @@ -95,7 +96,12 @@ function getTraceFlags( export class B3MultiPropagator implements TextMapPropagator { inject(context: Context, carrier: unknown, setter: TextMapSetter) { const spanContext = getSpanContext(context); - if (!spanContext || !isSpanContextValid(spanContext)) return; + if ( + !spanContext || + !isSpanContextValid(spanContext) || + isInstrumentationSuppressed(context) + ) + return; const debug = context.getValue(B3_DEBUG_FLAG_KEY); setter.set(carrier, X_B3_TRACE_ID, spanContext.traceId); diff --git a/packages/opentelemetry-propagator-b3/src/B3Propagator.ts b/packages/opentelemetry-propagator-b3/src/B3Propagator.ts index a1b09635f8..7b40ef0527 100644 --- a/packages/opentelemetry-propagator-b3/src/B3Propagator.ts +++ b/packages/opentelemetry-propagator-b3/src/B3Propagator.ts @@ -16,12 +16,14 @@ import { Context, + isInstrumentationSuppressed, TextMapGetter, TextMapPropagator, TextMapSetter, } from '@opentelemetry/api'; import { B3MultiPropagator } from './B3MultiPropagator'; -import { B3SinglePropagator, B3_CONTEXT_HEADER } from './B3SinglePropagator'; +import { B3SinglePropagator } from './B3SinglePropagator'; +import { B3_CONTEXT_HEADER } from './constants'; import { B3InjectEncoding, B3PropagatorConfig } from './types'; /** @@ -52,6 +54,9 @@ export class B3Propagator implements TextMapPropagator { } inject(context: Context, carrier: unknown, setter: TextMapSetter) { + if (isInstrumentationSuppressed(context)) { + return; + } this._inject(context, carrier, setter); } diff --git a/packages/opentelemetry-propagator-b3/src/B3SinglePropagator.ts b/packages/opentelemetry-propagator-b3/src/B3SinglePropagator.ts index 8e751cfb79..acd36820e8 100644 --- a/packages/opentelemetry-propagator-b3/src/B3SinglePropagator.ts +++ b/packages/opentelemetry-propagator-b3/src/B3SinglePropagator.ts @@ -17,6 +17,7 @@ import { Context, getSpanContext, + isInstrumentationSuppressed, isSpanContextValid, isValidSpanId, isValidTraceId, @@ -26,11 +27,9 @@ import { TextMapSetter, TraceFlags, } from '@opentelemetry/api'; +import { B3_CONTEXT_HEADER } from './constants'; import { B3_DEBUG_FLAG_KEY } from './common'; -/** B3 single-header name */ -export const B3_CONTEXT_HEADER = 'b3'; - const B3_CONTEXT_REGEX = /((?:[0-9a-f]{16}){1,2})-([0-9a-f]{16})(?:-([01d](?![0-9a-f])))?(?:-([0-9a-f]{16}))?/; const PADDING = '0'.repeat(16); const SAMPLED_VALUES = new Set(['d', '1']); @@ -54,7 +53,12 @@ function convertToTraceFlags(samplingState: string | undefined): TraceFlags { export class B3SinglePropagator implements TextMapPropagator { inject(context: Context, carrier: unknown, setter: TextMapSetter) { const spanContext = getSpanContext(context); - if (!spanContext || !isSpanContextValid(spanContext)) return; + if ( + !spanContext || + !isSpanContextValid(spanContext) || + isInstrumentationSuppressed(context) + ) + return; const samplingState = context.getValue(B3_DEBUG_FLAG_KEY) || spanContext.traceFlags & 0x1; diff --git a/packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts b/packages/opentelemetry-propagator-b3/src/constants.ts similarity index 65% rename from packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts rename to packages/opentelemetry-propagator-b3/src/constants.ts index 7faf3e8b88..cd60c865cf 100644 --- a/packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts +++ b/packages/opentelemetry-propagator-b3/src/constants.ts @@ -14,10 +14,12 @@ * limitations under the License. */ -import { runTests } from '@opentelemetry/grpc-utils/test'; -import { plugin, GrpcJsPlugin } from '../src/grpcJs'; -import * as grpc from '@grpc/grpc-js'; +/** B3 single-header key */ +export const B3_CONTEXT_HEADER = 'b3'; -describe(`#${GrpcJsPlugin.component}`, () => { - runTests(plugin, GrpcJsPlugin.component, grpc, 12346); -}); +/* b3 multi-header keys */ +export const X_B3_TRACE_ID = 'x-b3-traceid'; +export const X_B3_SPAN_ID = 'x-b3-spanid'; +export const X_B3_SAMPLED = 'x-b3-sampled'; +export const X_B3_PARENT_SPAN_ID = 'x-b3-parentspanid'; +export const X_B3_FLAGS = 'x-b3-flags'; diff --git a/packages/opentelemetry-propagator-b3/src/index.ts b/packages/opentelemetry-propagator-b3/src/index.ts index 3fb23f4fda..5bc5069e1f 100644 --- a/packages/opentelemetry-propagator-b3/src/index.ts +++ b/packages/opentelemetry-propagator-b3/src/index.ts @@ -15,6 +15,5 @@ */ export * from './B3Propagator'; -export * from './B3SinglePropagator'; -export * from './B3MultiPropagator'; +export * from './constants'; export * from './types'; diff --git a/packages/opentelemetry-propagator-b3/src/version.ts b/packages/opentelemetry-propagator-b3/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-propagator-b3/src/version.ts +++ b/packages/opentelemetry-propagator-b3/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-propagator-b3/test/B3MultiPropagator.test.ts b/packages/opentelemetry-propagator-b3/test/B3MultiPropagator.test.ts index 211fd48c3e..a415c22603 100644 --- a/packages/opentelemetry-propagator-b3/test/B3MultiPropagator.test.ts +++ b/packages/opentelemetry-propagator-b3/test/B3MultiPropagator.test.ts @@ -20,18 +20,19 @@ import { getSpanContext, setSpanContext, SpanContext, + suppressInstrumentation, TraceFlags, } from '@opentelemetry/api'; import { ROOT_CONTEXT } from '@opentelemetry/api'; import * as assert from 'assert'; +import { B3MultiPropagator } from '../src/B3MultiPropagator'; import { - B3MultiPropagator, X_B3_FLAGS, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, -} from '../src/B3MultiPropagator'; +} from '../src/constants'; import { B3_DEBUG_FLAG_KEY } from '../src/common'; describe('B3MultiPropagator', () => { @@ -138,6 +139,23 @@ describe('B3MultiPropagator', () => { assert.deepStrictEqual(carrier[X_B3_FLAGS], undefined); assert.deepStrictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); }); + + it('should not inject if instrumentation suppressed', () => { + const spanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + b3Propagator.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + assert.strictEqual(carrier[X_B3_TRACE_ID], undefined); + assert.strictEqual(carrier[X_B3_SPAN_ID], undefined); + assert.strictEqual(carrier[X_B3_FLAGS], undefined); + assert.strictEqual(carrier[X_B3_PARENT_SPAN_ID], undefined); + }); }); describe('.extract()', () => { diff --git a/packages/opentelemetry-propagator-b3/test/B3Propagator.test.ts b/packages/opentelemetry-propagator-b3/test/B3Propagator.test.ts index e68216e187..bf0f89ff38 100644 --- a/packages/opentelemetry-propagator-b3/test/B3Propagator.test.ts +++ b/packages/opentelemetry-propagator-b3/test/B3Propagator.test.ts @@ -23,17 +23,18 @@ import { getSpanContext, setSpanContext, ROOT_CONTEXT, + suppressInstrumentation, } from '@opentelemetry/api'; import { B3Propagator } from '../src/B3Propagator'; import { B3InjectEncoding } from '../src/types'; -import { B3_CONTEXT_HEADER } from '../src/B3SinglePropagator'; import { + B3_CONTEXT_HEADER, X_B3_FLAGS, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, -} from '../src/B3MultiPropagator'; +} from '../src/constants'; describe('B3Propagator', () => { let propagator: B3Propagator; @@ -88,6 +89,24 @@ describe('B3Propagator', () => { assert.strictEqual(carrier[X_B3_SPAN_ID], 'e457b5a2e4d86bd1'); assert.strictEqual(carrier[X_B3_SAMPLED], '1'); }); + + it('should not inject if instrumentation suppressed', () => { + propagator = new B3Propagator(); + + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, + }; + + propagator.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + + assert.strictEqual(carrier[B3_CONTEXT_HEADER], undefined); + }); }); describe('.extract()', () => { diff --git a/packages/opentelemetry-propagator-b3/test/B3SinglePropagator.test.ts b/packages/opentelemetry-propagator-b3/test/B3SinglePropagator.test.ts index 02009821d6..4a6a65b8ac 100644 --- a/packages/opentelemetry-propagator-b3/test/B3SinglePropagator.test.ts +++ b/packages/opentelemetry-propagator-b3/test/B3SinglePropagator.test.ts @@ -23,13 +23,12 @@ import { setSpanContext, SpanContext, TraceFlags, + suppressInstrumentation, } from '@opentelemetry/api'; import { ROOT_CONTEXT } from '@opentelemetry/api'; import * as assert from 'assert'; -import { - B3SinglePropagator, - B3_CONTEXT_HEADER, -} from '../src/B3SinglePropagator'; +import { B3SinglePropagator } from '../src/B3SinglePropagator'; +import { B3_CONTEXT_HEADER } from '../src/constants'; import { B3_DEBUG_FLAG_KEY } from '../src/common'; describe('B3SinglePropagator', () => { @@ -125,6 +124,22 @@ describe('B3SinglePropagator', () => { assert.strictEqual(carrier[B3_CONTEXT_HEADER], undefined); }); + + it('does not inject if instrumentation is suppressed', () => { + const spanContext: SpanContext = { + traceId: '80f198ee56343ba864fe8b2a57d3eff7', + spanId: 'e457b5a2e4d86bd1', + traceFlags: TraceFlags.SAMPLED, + }; + + propagator.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + + assert.strictEqual(carrier[B3_CONTEXT_HEADER], undefined); + }); }); describe('.extract', () => { diff --git a/packages/opentelemetry-grpc-utils/.eslintignore b/packages/opentelemetry-propagator-jaeger/.eslintignore similarity index 100% rename from packages/opentelemetry-grpc-utils/.eslintignore rename to packages/opentelemetry-propagator-jaeger/.eslintignore diff --git a/packages/opentelemetry-grpc-utils/.eslintrc.js b/packages/opentelemetry-propagator-jaeger/.eslintrc.js similarity index 67% rename from packages/opentelemetry-grpc-utils/.eslintrc.js rename to packages/opentelemetry-propagator-jaeger/.eslintrc.js index f726f3becb..b23ee98fc0 100644 --- a/packages/opentelemetry-grpc-utils/.eslintrc.js +++ b/packages/opentelemetry-propagator-jaeger/.eslintrc.js @@ -1,7 +1,8 @@ module.exports = { "env": { "mocha": true, - "node": true + "commonjs": true, + "browser": true, }, ...require('../../eslint.config.js') } diff --git a/packages/opentelemetry-grpc-utils/LICENSE b/packages/opentelemetry-propagator-jaeger/LICENSE similarity index 100% rename from packages/opentelemetry-grpc-utils/LICENSE rename to packages/opentelemetry-propagator-jaeger/LICENSE diff --git a/packages/opentelemetry-propagator-jaeger/README.md b/packages/opentelemetry-propagator-jaeger/README.md new file mode 100644 index 0000000000..4990714616 --- /dev/null +++ b/packages/opentelemetry-propagator-jaeger/README.md @@ -0,0 +1,61 @@ +# OpenTelemetry Propagator Jaeger + +[![NPM Published Version][npm-img]][npm-url] +[![dependencies][dependencies-image]][dependencies-url] +[![devDependencies][devDependencies-image]][devDependencies-url] +[![Apache License][license-image]][license-image] + +OpenTelemetry Jaeger propagator provides HTTP header propagation for systems that are using Jaeger HTTP header format. + +Format: +{trace-id}:{span-id}:{parent-span-id}:{flags} + +* {trace-id} + * 64-bit or 128-bit random number in base16 format. + * Can be variable length, shorter values are 0-padded on the left. + * Value of 0 is invalid. + +* {span-id} + * 64-bit random number in base16 format. + +* {parent-span-id} + * Set to 0 because this field is deprecated. +* {flags} + * One byte bitmap, as two hex digits. + +Example of usage: + +```javascript +const { NodeTracerProvider } = require('@opentelemetry/node'); +const { JaegerHttpTracePropagator } = require('@opentelemetry/propagator-jaeger'); + +const provider = new NodeTracerProvider(); +provider.register({ + // Use Jaeger propagator + propagator: new JaegerHttpTracePropagator() +}); +``` + +## Trace on Jaeger UI + +![example image](jaeger-tracing.png) + +## 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 +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-propagator-jaeger +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-propagator-jaeger +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-propagator-jaeger +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-propagator-jaeger&type=dev +[npm-url]: https://www.npmjs.com/package/@opentelemetry/propagator-jaeger +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fpropagator-jaeger.svg diff --git a/packages/opentelemetry-propagator-jaeger/jaeger-tracing.png b/packages/opentelemetry-propagator-jaeger/jaeger-tracing.png new file mode 100644 index 0000000000..0c8693a2b5 Binary files /dev/null and b/packages/opentelemetry-propagator-jaeger/jaeger-tracing.png differ diff --git a/packages/opentelemetry-semantic-conventions/src/trace/exception.ts b/packages/opentelemetry-propagator-jaeger/karma.conf.js similarity index 65% rename from packages/opentelemetry-semantic-conventions/src/trace/exception.ts rename to packages/opentelemetry-propagator-jaeger/karma.conf.js index cf7dc596be..bf2a41b799 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/exception.ts +++ b/packages/opentelemetry-propagator-jaeger/karma.conf.js @@ -1,11 +1,11 @@ -/* +/*! * 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 + * 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, @@ -14,10 +14,11 @@ * limitations under the License. */ -export const ExceptionAttribute = { - MESSAGE: 'exception.message', - STACKTRACE: 'exception.stacktrace', - TYPE: 'exception.type', -}; +const karmaWebpackConfig = require('../../karma.webpack'); +const karmaBaseConfig = require('../../karma.base'); -export const ExceptionEventName = 'exception'; +module.exports = (config) => { + config.set(Object.assign({}, karmaBaseConfig, { + webpack: karmaWebpackConfig, + })) +}; diff --git a/packages/opentelemetry-plugin-http/package.json b/packages/opentelemetry-propagator-jaeger/package.json similarity index 52% rename from packages/opentelemetry-plugin-http/package.json rename to packages/opentelemetry-propagator-jaeger/package.json index 5183fa4be5..079606a248 100644 --- a/packages/opentelemetry-plugin-http/package.json +++ b/packages/opentelemetry-propagator-jaeger/package.json @@ -1,33 +1,35 @@ { - "name": "@opentelemetry/plugin-http", - "version": "0.18.0", - "description": "OpenTelemetry http automatic instrumentation package.", + "name": "@opentelemetry/propagator-jaeger", + "version": "0.18.2", + "description": "OpenTelemetry Jaeger propagator provides HTTP header propagation for systems that are using Jaeger HTTP header format.", "main": "build/src/index.js", "types": "build/src/index.d.ts", "repository": "open-telemetry/opentelemetry-js", "scripts": { "compile": "tsc --build", + "test": "nyc ts-mocha -p tsconfig.json 'test/**/*.test.ts' --exclude 'test/index-webpack.ts'", + "test:browser": "nyc karma start --single-run", + "tdd": "npm run tdd:node", + "tdd:node": "npm run test -- --watch-extensions ts --watch", + "tdd:browser": "karma start", + "codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../", "clean": "tsc --build --clean", - "test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts", - "tdd": "npm run test -- --watch-extensions ts --watch", "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 --build --watch" }, "keywords": [ "opentelemetry", - "http", "nodejs", "tracing", "profiling", - "plugin" + "jaeger" ], "author": "OpenTelemetry Authors", "license": "Apache-2.0", "engines": { - "node": ">=8.0.0" + "node": ">=8.5.0" }, "files": [ "build/src/**/*.js", @@ -41,38 +43,34 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/context-async-hooks": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", - "@types/got": "9.6.11", + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", - "@types/request-promise-native": "1.0.17", - "@types/semver": "7.3.4", - "@types/shimmer": "1.0.1", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", - "@types/superagent": "4.1.10", - "axios": "0.21.1", + "@types/webpack-env": "1.16.0", "codecov": "3.8.1", - "got": "9.6.0", "gts": "3.1.0", + "istanbul-instrumenter-loader": "3.0.1", + "karma": "5.2.3", + "karma-chrome-launcher": "3.1.0", + "karma-coverage-istanbul-reporter": "3.0.3", + "karma-mocha": "2.0.1", + "karma-spec-reporter": "0.0.32", + "karma-webpack": "5.0.0", "mocha": "7.2.0", - "nock": "12.0.3", "nyc": "15.1.0", - "request": "2.88.2", - "request-promise-native": "1.0.9", "rimraf": "3.0.2", - "sinon": "9.2.4", - "superagent": "6.1.0", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4", + "webpack": "4.46.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "semver": "^7.1.3", - "shimmer": "^1.2.1" + "@opentelemetry/core": "0.18.2" } } diff --git a/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTracePropagator.ts b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTracePropagator.ts new file mode 100644 index 0000000000..2afa35113f --- /dev/null +++ b/packages/opentelemetry-propagator-jaeger/src/JaegerHttpTracePropagator.ts @@ -0,0 +1,106 @@ +/* + * 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, + getSpanContext, + setSpanContext, + SpanContext, + TraceFlags, + TextMapGetter, + TextMapPropagator, + TextMapSetter, + isInstrumentationSuppressed, +} from '@opentelemetry/api'; + +export const UBER_TRACE_ID_HEADER = 'uber-trace-id'; + +/** + * Propagates {@link SpanContext} through Trace Context format propagation. + * {trace-id}:{span-id}:{parent-span-id}:{flags} + * {trace-id} + * 64-bit or 128-bit random number in base16 format. + * Can be variable length, shorter values are 0-padded on the left. + * Value of 0 is invalid. + * {span-id} + * 64-bit random number in base16 format. + * {parent-span-id} + * Set to 0 because this field is deprecated. + * {flags} + * One byte bitmap, as two hex digits. + * Inspired by jaeger-client-node project. + */ +export class JaegerHttpTracePropagator implements TextMapPropagator { + private readonly _jaegerTraceHeader: string; + + /** + * @param {string} [customTraceHeader="uber-trace-id"] - HTTP header to inject\extract trace from. + **/ + constructor(customTraceHeader?: string) { + this._jaegerTraceHeader = customTraceHeader || UBER_TRACE_ID_HEADER; + } + + inject(context: Context, carrier: unknown, setter: TextMapSetter) { + const spanContext = getSpanContext(context); + if (!spanContext || isInstrumentationSuppressed(context)) return; + + const traceFlags = `0${(spanContext.traceFlags || TraceFlags.NONE).toString( + 16 + )}`; + + setter.set( + carrier, + this._jaegerTraceHeader, + `${spanContext.traceId}:${spanContext.spanId}:0:${traceFlags}` + ); + } + + extract(context: Context, carrier: unknown, getter: TextMapGetter): Context { + const uberTraceIdHeader = getter.get(carrier, this._jaegerTraceHeader); + const uberTraceId = Array.isArray(uberTraceIdHeader) + ? uberTraceIdHeader[0] + : uberTraceIdHeader; + + if (typeof uberTraceId !== 'string') return context; + + const spanContext = deserializeSpanContext(uberTraceId); + if (!spanContext) return context; + + return setSpanContext(context, spanContext); + } + + fields(): string[] { + return [this._jaegerTraceHeader]; + } +} + +/** + * @param {string} serializedString - a serialized span context. + * @return {SpanContext} - returns a span context represented by the serializedString. + **/ +function deserializeSpanContext(serializedString: string): SpanContext | null { + const headers = decodeURIComponent(serializedString).split(':'); + if (headers.length !== 4) { + return null; + } + + const [_traceId, spanId, , flags] = headers; + + const traceId = _traceId.padStart(32, '0'); + const traceFlags = flags.match(/^[0-9a-f]{2}$/i) ? parseInt(flags) & 1 : 1; + + return { traceId, spanId, isRemote: true, traceFlags }; +} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/index.ts b/packages/opentelemetry-propagator-jaeger/src/index.ts similarity index 93% rename from packages/opentelemetry-plugin-grpc-js/src/server/index.ts rename to packages/opentelemetry-propagator-jaeger/src/index.ts index 99bb1743c1..c5c818be95 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/index.ts +++ b/packages/opentelemetry-propagator-jaeger/src/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from './patchServer'; +export * from './JaegerHttpTracePropagator'; diff --git a/packages/opentelemetry-plugin-grpc-js/src/version.ts b/packages/opentelemetry-propagator-jaeger/src/version.ts similarity index 95% rename from packages/opentelemetry-plugin-grpc-js/src/version.ts rename to packages/opentelemetry-propagator-jaeger/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/version.ts +++ b/packages/opentelemetry-propagator-jaeger/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTracePropagator.test.ts b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTracePropagator.test.ts new file mode 100644 index 0000000000..21f81bf10b --- /dev/null +++ b/packages/opentelemetry-propagator-jaeger/test/JaegerHttpTracePropagator.test.ts @@ -0,0 +1,253 @@ +/* + * 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 { + defaultTextMapGetter, + defaultTextMapSetter, + getSpanContext, + ROOT_CONTEXT, + setSpanContext, + SpanContext, + suppressInstrumentation, + TextMapGetter, + TraceFlags, +} from '@opentelemetry/api'; +import * as assert from 'assert'; +import { + JaegerHttpTracePropagator, + UBER_TRACE_ID_HEADER, +} from '../src/JaegerHttpTracePropagator'; + +describe('JaegerHttpTracePropagator', () => { + const jaegerHttpTracePropagator = new JaegerHttpTracePropagator(); + const customHeader = 'new-header'; + const customJaegerHttpTracePropagator = new JaegerHttpTracePropagator( + customHeader + ); + let carrier: { [key: string]: unknown }; + + beforeEach(() => { + carrier = {}; + }); + + describe('.inject()', () => { + it('should set uber trace id header', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + jaegerHttpTracePropagator.inject( + setSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultTextMapSetter + ); + assert.deepStrictEqual( + carrier[UBER_TRACE_ID_HEADER], + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' + ); + }); + + it('should use custom header if provided', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + customJaegerHttpTracePropagator.inject( + setSpanContext(ROOT_CONTEXT, spanContext), + carrier, + defaultTextMapSetter + ); + assert.deepStrictEqual( + carrier[customHeader], + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01' + ); + }); + + it('should not set uber trace id header if instrumentation suppressed', () => { + const spanContext: SpanContext = { + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', + traceFlags: TraceFlags.SAMPLED, + }; + + jaegerHttpTracePropagator.inject( + suppressInstrumentation(setSpanContext(ROOT_CONTEXT, spanContext)), + carrier, + defaultTextMapSetter + ); + assert.strictEqual(carrier[UBER_TRACE_ID_HEADER], undefined); + }); + }); + + describe('.extract()', () => { + it('should extract context of a sampled span from carrier', () => { + carrier[UBER_TRACE_ID_HEADER] = + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; + const extractedSpanContext = getSpanContext( + jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '6e0c63257de34c92', + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('should extract context of a sampled span from carrier with 1 bit flag', () => { + carrier[UBER_TRACE_ID_HEADER] = + '9c41e35aeb6d1272:45fd2a9709dadcf1:a13699e3fb724f40:1'; + const extractedSpanContext = getSpanContext( + jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '45fd2a9709dadcf1', + traceId: '00000000000000009c41e35aeb6d1272', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('should extract context of a sampled span from UTF-8 encoded carrier', () => { + carrier[UBER_TRACE_ID_HEADER] = + 'ac1f3dc3c2c0b06e%3A5ac292c4a11a163e%3Ac086aaa825821068%3A1'; + const extractedSpanContext = getSpanContext( + jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '5ac292c4a11a163e', + traceId: '0000000000000000ac1f3dc3c2c0b06e', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('should use custom header if provided', () => { + carrier[customHeader] = + 'd4cda95b652f4a1592b449d5929fda1b:6e0c63257de34c92:0:01'; + const extractedSpanContext = getSpanContext( + customJaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ); + + assert.deepStrictEqual(extractedSpanContext, { + spanId: '6e0c63257de34c92', + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + isRemote: true, + traceFlags: TraceFlags.SAMPLED, + }); + }); + + it('returns undefined if UBER_TRACE_ID_HEADER header is missing', () => { + assert.deepStrictEqual( + getSpanContext( + jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ), + undefined + ); + }); + + it('returns undefined if UBER_TRACE_ID_HEADER header is invalid', () => { + carrier[UBER_TRACE_ID_HEADER] = 'invalid!'; + assert.deepStrictEqual( + getSpanContext( + jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + defaultTextMapGetter + ) + ), + undefined + ); + }); + }); + + describe('.fields()', () => { + it('returns the default header if not customized', () => { + assert.deepStrictEqual(jaegerHttpTracePropagator.fields(), [ + 'uber-trace-id', + ]); + }); + it('returns the customized header if customized', () => { + assert.deepStrictEqual(customJaegerHttpTracePropagator.fields(), [ + customHeader, + ]); + }); + }); + + it('should fail gracefully on bad responses from getter', () => { + const ctx1 = jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + makeGetter(1) // not a number + ); + const ctx2 = jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + makeGetter([]) // empty array + ); + const ctx3 = jaegerHttpTracePropagator.extract( + ROOT_CONTEXT, + carrier, + makeGetter(undefined) // missing value + ); + + assert.ok(ctx1 === ROOT_CONTEXT); + assert.ok(ctx2 === ROOT_CONTEXT); + assert.ok(ctx3 === ROOT_CONTEXT); + }); +}); + +function makeGetter(value: any) { + const getter: TextMapGetter = { + get(carrier, key) { + return value; + }, + keys(carrier) { + if (carrier == null) { + return []; + } + return Object.keys(carrier); + }, + }; + return getter; +} diff --git a/packages/opentelemetry-grpc-utils/src/version.ts b/packages/opentelemetry-propagator-jaeger/test/index-webpack.ts similarity index 75% rename from packages/opentelemetry-grpc-utils/src/version.ts rename to packages/opentelemetry-propagator-jaeger/test/index-webpack.ts index 6fdb996caa..061a48ccfa 100644 --- a/packages/opentelemetry-grpc-utils/src/version.ts +++ b/packages/opentelemetry-propagator-jaeger/test/index-webpack.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const testsContext = require.context('.', true, /test$/); +testsContext.keys().forEach(testsContext); -// this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +const srcContext = require.context('.', true, /src$/); +srcContext.keys().forEach(srcContext); diff --git a/packages/opentelemetry-propagator-jaeger/tsconfig.json b/packages/opentelemetry-propagator-jaeger/tsconfig.json new file mode 100644 index 0000000000..a6c0bb2617 --- /dev/null +++ b/packages/opentelemetry-propagator-jaeger/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/**/*.ts", + "test/**/*.ts" + ], + "references": [ + { + "path": "../opentelemetry-core" + } + ] +} diff --git a/packages/opentelemetry-resource-detector-aws/package.json b/packages/opentelemetry-resource-detector-aws/package.json index 5e35e749cb..04f99c5a28 100644 --- a/packages/opentelemetry-resource-detector-aws/package.json +++ b/packages/opentelemetry-resource-detector-aws/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/resource-detector-aws", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry SDK resource detector for AWS", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -39,8 +39,9 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", @@ -48,14 +49,16 @@ "nock": "12.0.3", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/resources": "^0.18.0" + "@opentelemetry/core": "0.18.2", + "@opentelemetry/resources": "0.18.2" } } diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 54c0f63bb6..3375ee7557 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -66,7 +66,7 @@ export class AwsEksDetector implements Detector { await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); const k8scert = await AwsEksDetector.readFileAsync(this.K8S_CERT_PATH); - if (!this._isEks(k8scert)) { + if (!(await this._isEks(k8scert))) { return Resource.empty(); } diff --git a/packages/opentelemetry-resource-detector-aws/src/version.ts b/packages/opentelemetry-resource-detector-aws/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-resource-detector-aws/src/version.ts +++ b/packages/opentelemetry-resource-detector-aws/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 0c57854072..06dc603824 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -247,8 +247,7 @@ describe('awsEksDetector', () => { }); describe('on unsuccesful request', () => { - it('should throw when receiving error response code', async () => { - const expectedError = new Error('EKS metadata api request timed out.'); + it('should return an empty resource when timed out', async () => { fileStub = sinon .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); @@ -265,17 +264,14 @@ describe('awsEksDetector', () => { .delayConnection(2500) .reply(200, () => mockedAwsAuth); - try { - await awsEksDetector.detect(); - } catch (err) { - assert.deepStrictEqual(err, expectedError); - } - + const resource: Resource = await awsEksDetector.detect(); scope.done(); - }); - it('should return an empty resource when timed out', async () => { - const expectedError = new Error('Failed to load page, status code: 404'); + assert.ok(resource); + assertEmptyResource(resource); + }).timeout(awsEksDetector.TIMEOUT_MS + 100); + + it('should return an empty resource when receiving error response code', async () => { fileStub = sinon .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); @@ -291,13 +287,11 @@ describe('awsEksDetector', () => { .matchHeader('Authorization', k8s_token) .reply(404, () => new Error()); - try { - await awsEksDetector.detect(); - } catch (err) { - assert.deepStrictEqual(err, expectedError); - } - + const resource: Resource = await awsEksDetector.detect(); scope.done(); + + assert.ok(resource); + assertEmptyResource(resource); }); }); }); diff --git a/packages/opentelemetry-resource-detector-gcp/package.json b/packages/opentelemetry-resource-detector-gcp/package.json index 2190979551..ea481c5402 100644 --- a/packages/opentelemetry-resource-detector-gcp/package.json +++ b/packages/opentelemetry-resource-detector-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/resource-detector-gcp", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry SDK resource detector for GCP", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -39,9 +39,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/core": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/core": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "codecov": "3.8.1", "gts": "3.1.0", @@ -51,11 +52,13 @@ "rimraf": "3.0.2", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/resources": "^0.18.0", + "@opentelemetry/resources": "0.18.2", "gcp-metadata": "^4.1.4", "semver": "7.3.5" } diff --git a/packages/opentelemetry-resource-detector-gcp/src/version.ts b/packages/opentelemetry-resource-detector-gcp/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-resource-detector-gcp/src/version.ts +++ b/packages/opentelemetry-resource-detector-gcp/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-resources/package.json b/packages/opentelemetry-resources/package.json index ff11b034fe..d0c0c2b0d2 100644 --- a/packages/opentelemetry-resources/package.json +++ b/packages/opentelemetry-resources/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/resources", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry SDK resources", "main": "build/src/index.js", "browser": { @@ -43,8 +43,9 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", @@ -52,13 +53,15 @@ "nock": "12.0.3", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0" + "@opentelemetry/core": "0.18.2" } } diff --git a/packages/opentelemetry-resources/src/version.ts b/packages/opentelemetry-resources/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-resources/src/version.ts +++ b/packages/opentelemetry-resources/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-sdk-node/README.md b/packages/opentelemetry-sdk-node/README.md index 722086cec9..156352b11b 100644 --- a/packages/opentelemetry-sdk-node/README.md +++ b/packages/opentelemetry-sdk-node/README.md @@ -9,7 +9,7 @@ This package provides the full OpenTelemetry SDK for Node.js including tracing a ## Quick Start -To get started you need to install `@opentelemetry/sdk-node`, a metrics and/or tracing exporter, and any appropriate plugins for the node modules used by your application. +To get started you need to install `@opentelemetry/sdk-node`, a metrics and/or tracing exporter, and any appropriate instrumentation for the node modules used by your application. ### Installation @@ -21,10 +21,11 @@ $ # Install exporters and plugins $ npm install \ @opentelemetry/exporter-jaeger \ # add tracing exporters as needed @opentelemetry/exporter-prometheus # add metrics exporters as needed - @opentelemetry/plugin-http # add plugins as needed + @opentelemetry/instrumentation-http # add instrumentations as needed $ # or install all officially supported core and contrib plugins -$ npm install @opentelemetry/plugins-node-core-and-contrib +$ npm install @opentelemetry/auto-instrumentations-node + ``` > Note: this example is for Node.js. See [examples/tracer-web](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web) for a browser example. @@ -39,6 +40,7 @@ This example uses Jaeger and Prometheus, but exporters exist for [other tracing const opentelemetry = require("@opentelemetry/sdk-node"); const { JaegerExporter } = require("@opentelemetry/exporter-jaeger"); const { PrometheusExporter } = require("@opentelemetry/exporter-prometheus"); +const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node"); const jaegerExporter = new JaegerExporter({ serviceName: 'my-service', @@ -50,7 +52,8 @@ const sdk = new opentelemetry.NodeSDK({ traceExporter: jaegerExporter, // Optional - If omitted, the metrics SDK will not be initialized metricExporter: prometheusExporter, - + // Optional - you can use the metapackage or load each instrumentation individually + instrumentations: [getNodeAutoInstrumentations()], // See the Configuration section below for additional configuration options }); @@ -103,9 +106,11 @@ Configure a metric exporter. If an exporter is not configured, the metrics SDK w Configure an interval for metrics export in ms. Default: 60,000 (60 seconds) -### plugins +### instrumentations -Configure plugins. By default, all plugins which are installed and in the [Default Plugins List](../opentelemetry-node/src/config.ts#L29) will be enabled. +Configure instrumentations. By default none of the instrumentation is enabled, +if you want to enable them you can use either [metapackage](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/metapackages/auto-instrumentations-node) +or configure each instrumentation individually. ### resource diff --git a/packages/opentelemetry-sdk-node/package.json b/packages/opentelemetry-sdk-node/package.json index 7bd4e5ba66..64239b57dc 100644 --- a/packages/opentelemetry-sdk-node/package.json +++ b/packages/opentelemetry-sdk-node/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/sdk-node", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry SDK for Node.js", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -40,22 +40,25 @@ "access": "public" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/api-metrics": "^0.18.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/instrumentation": "^0.18.0", - "@opentelemetry/metrics": "^0.18.0", - "@opentelemetry/node": "^0.18.0", - "@opentelemetry/resource-detector-aws": "^0.18.0", - "@opentelemetry/resource-detector-gcp": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/api-metrics": "0.18.2", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/instrumentation": "0.18.2", + "@opentelemetry/metrics": "0.18.2", + "@opentelemetry/node": "0.18.2", + "@opentelemetry/resource-detector-aws": "0.18.2", + "@opentelemetry/resource-detector-gcp": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/tracing": "0.18.2", "nock": "12.0.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "devDependencies": { - "@opentelemetry/context-async-hooks": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-async-hooks": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/semver": "7.3.4", "@types/sinon": "9.0.11", "codecov": "3.8.1", @@ -65,9 +68,9 @@ "mocha": "7.2.0", "nyc": "15.1.0", "semver": "7.3.5", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", - "typescript": "4.2.3" + "typescript": "4.2.4" } } diff --git a/packages/opentelemetry-sdk-node/src/sdk.ts b/packages/opentelemetry-sdk-node/src/sdk.ts index 0137ce46bd..d717415fd9 100644 --- a/packages/opentelemetry-sdk-node/src/sdk.ts +++ b/packages/opentelemetry-sdk-node/src/sdk.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { diag, TextMapPropagator } from '@opentelemetry/api'; +import { TextMapPropagator } from '@opentelemetry/api'; import { metrics } from '@opentelemetry/api-metrics'; import { ContextManager } from '@opentelemetry/api'; import { MeterConfig, MeterProvider } from '@opentelemetry/metrics'; @@ -102,9 +102,6 @@ export class NodeSDK { let instrumentations: InstrumentationOption[] = []; if (configuration.instrumentations) { instrumentations = configuration.instrumentations; - } else if (configuration.plugins) { - diag.error('plugins option is deprecated'); - instrumentations = configuration.plugins; } this._instrumentations = instrumentations; } diff --git a/packages/opentelemetry-sdk-node/src/types.ts b/packages/opentelemetry-sdk-node/src/types.ts index c017cf0322..a34c1b5140 100644 --- a/packages/opentelemetry-sdk-node/src/types.ts +++ b/packages/opentelemetry-sdk-node/src/types.ts @@ -33,8 +33,6 @@ export interface NodeSDKConfiguration { metricProcessor: Processor; metricExporter: MetricExporter; metricInterval: number; - /* Deprecated */ - plugins: InstrumentationOption[]; instrumentations: InstrumentationOption[]; resource: Resource; sampler: Sampler; diff --git a/packages/opentelemetry-sdk-node/src/version.ts b/packages/opentelemetry-sdk-node/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-sdk-node/src/version.ts +++ b/packages/opentelemetry-sdk-node/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-sdk-node/test/sdk.test.ts b/packages/opentelemetry-sdk-node/test/sdk.test.ts index 6b8e59d5d1..f9274caad3 100644 --- a/packages/opentelemetry-sdk-node/test/sdk.test.ts +++ b/packages/opentelemetry-sdk-node/test/sdk.test.ts @@ -33,7 +33,6 @@ import { NoopContextManager } from '@opentelemetry/api'; import { CompositePropagator } from '@opentelemetry/core'; import { ConsoleMetricExporter, MeterProvider } from '@opentelemetry/metrics'; import { NodeTracerProvider } from '@opentelemetry/node'; -import * as NodeConfig from '@opentelemetry/instrumentation/build/src/platform/node/old/autoLoader'; import { awsEc2Detector } from '@opentelemetry/resource-detector-aws'; import { resetIsAvailableCache } from '@opentelemetry/resource-detector-gcp'; import { Resource } from '@opentelemetry/resources'; @@ -91,8 +90,6 @@ const DefaultContextManager = semver.gte(process.version, '14.8.0') describe('Node SDK', () => { before(() => { - // Disable attempted load of default plugins - Sinon.replace(NodeConfig, 'DEFAULT_INSTRUMENTATION_PLUGINS', {}); nock.disableNetConnect(); }); diff --git a/packages/opentelemetry-semantic-conventions/package.json b/packages/opentelemetry-semantic-conventions/package.json index d0500761a9..3fc06d8c39 100644 --- a/packages/opentelemetry-semantic-conventions/package.json +++ b/packages/opentelemetry-semantic-conventions/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/semantic-conventions", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry semantic conventions", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -40,7 +40,7 @@ }, "devDependencies": { "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "codecov": "3.8.1", "gts": "3.1.0", @@ -48,9 +48,9 @@ "nock": "12.0.3", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", + "sinon": "10.0.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3" + "typescript": "4.2.4" } } diff --git a/packages/opentelemetry-semantic-conventions/src/index.ts b/packages/opentelemetry-semantic-conventions/src/index.ts index ae2998a38c..6f9f387e23 100644 --- a/packages/opentelemetry-semantic-conventions/src/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/index.ts @@ -15,3 +15,4 @@ */ export * from './trace'; +export * from './resource'; diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts new file mode 100644 index 0000000000..5c719f07c5 --- /dev/null +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts @@ -0,0 +1,514 @@ +/* + * 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 ResourceAttributes = { + /** + * Name of the cloud provider. + */ + CLOUD_PROVIDER: 'cloud.provider', + + /** + * The cloud account ID the resource is assigned to. + */ + CLOUD_ACCOUNT_ID: 'cloud.account.id', + + /** + * The geographical region the resource is running. Refer to your provider's docs to see the available regions, for example [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), or [Google Cloud regions](https://cloud.google.com/about/locations). + */ + CLOUD_REGION: 'cloud.region', + + /** + * Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. + * + * Note: Availability zones are called "zones" on Google Cloud. + */ + CLOUD_AVAILABILITY_ZONE: 'cloud.availability_zone', + + /** + * The cloud infrastructure resource in use. + * + * Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. + */ + CLOUD_INFRASTRUCTURE_SERVICE: 'cloud.infrastructure_service', + + /** + * The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). + */ + AWS_ECS_CONTAINER_ARN: 'aws.ecs.container.arn', + + /** + * The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). + */ + AWS_ECS_CLUSTER_ARN: 'aws.ecs.cluster.arn', + + /** + * The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. + */ + AWS_ECS_LAUNCHTYPE: 'aws.ecs.launchtype', + + /** + * The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). + */ + AWS_ECS_TASK_ARN: 'aws.ecs.task.arn', + + /** + * The task definition family this task definition is a member of. + */ + AWS_ECS_TASK_FAMILY: 'aws.ecs.task.family', + + /** + * The ARN of an EKS cluster. + */ + AWS_EKS_CLUSTER_ARN: 'aws.eks.cluster.arn', + + /** + * The name(s) of the AWS log group(s) an application is writing to. + * + * Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. + */ + AWS_LOG_GROUP_NAMES: 'aws.log.group.names', + + /** + * The Amazon Resource Name(s) (ARN) of the AWS log group(s). + * + * Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). + */ + AWS_LOG_GROUP_ARNS: 'aws.log.group.arns', + + /** + * The name(s) of the AWS log stream(s) an application is writing to. + */ + AWS_LOG_STREAM_NAMES: 'aws.log.stream.names', + + /** + * The ARN(s) of the AWS log stream(s). + * + * Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. + */ + AWS_LOG_STREAM_ARNS: 'aws.log.stream.arns', + + /** + * Container name. + */ + CONTAINER_NAME: 'container.name', + + /** + * Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/reference/run/#container-identification). The UUID might be abbreviated. + */ + CONTAINER_ID: 'container.id', + + /** + * The container runtime managing this container. + */ + CONTAINER_RUNTIME: 'container.runtime', + + /** + * Name of the image the container was built on. + */ + CONTAINER_IMAGE_NAME: 'container.image.name', + + /** + * Container image tag. + */ + CONTAINER_IMAGE_TAG: 'container.image.tag', + + /** + * Name of the [deployment environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka deployment tier). + */ + DEPLOYMENT_ENVIRONMENT: 'deployment.environment', + + /** + * The name of the function being executed. + */ + FAAS_NAME: 'faas.name', + + /** + * The unique ID of the function being executed. + * + * Note: For example, in AWS Lambda this field corresponds to the [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) value, in GCP to the URI of the resource, and in Azure to the [FunctionDirectory](https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function) field. + */ + FAAS_ID: 'faas.id', + + /** + * The version string of the function being executed as defined in [Version Attributes](../../resource/semantic_conventions/README.md#version-attributes). + */ + FAAS_VERSION: 'faas.version', + + /** + * The execution environment ID as a string. + */ + FAAS_INSTANCE: 'faas.instance', + + /** + * The amount of memory available to the serverless function in MiB. + * + * Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information. + */ + FAAS_MAX_MEMORY: 'faas.max_memory', + + /** + * Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. + */ + HOST_ID: 'host.id', + + /** + * Name of the host. On Unix systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. + */ + HOST_NAME: 'host.name', + + /** + * Type of host. For Cloud, this must be the machine type. + */ + HOST_TYPE: 'host.type', + + /** + * The CPU architecture the host system is running on. + */ + HOST_ARCH: 'host.arch', + + /** + * Name of the VM image or OS install the host was instantiated from. + */ + HOST_IMAGE_NAME: 'host.image.name', + + /** + * VM image ID. For Cloud, this value is from the provider. + */ + HOST_IMAGE_ID: 'host.image.id', + + /** + * The version string of the VM image as defined in [Version Attributes](README.md#version-attributes). + */ + HOST_IMAGE_VERSION: 'host.image.version', + + /** + * The name of the cluster. + */ + K8S_CLUSTER_NAME: 'k8s.cluster.name', + + /** + * The name of the Node. + */ + K8S_NODE_NAME: 'k8s.node.name', + + /** + * The UID of the Node. + */ + K8S_NODE_UID: 'k8s.node.uid', + + /** + * The name of the namespace that the pod is running in. + */ + K8S_NAMESPACE_NAME: 'k8s.namespace.name', + + /** + * The UID of the Pod. + */ + K8S_POD_UID: 'k8s.pod.uid', + + /** + * The name of the Pod. + */ + K8S_POD_NAME: 'k8s.pod.name', + + /** + * The name of the Container in a Pod template. + */ + K8S_CONTAINER_NAME: 'k8s.container.name', + + /** + * The UID of the ReplicaSet. + */ + K8S_REPLICASET_UID: 'k8s.replicaset.uid', + + /** + * The name of the ReplicaSet. + */ + K8S_REPLICASET_NAME: 'k8s.replicaset.name', + + /** + * The UID of the Deployment. + */ + K8S_DEPLOYMENT_UID: 'k8s.deployment.uid', + + /** + * The name of the Deployment. + */ + K8S_DEPLOYMENT_NAME: 'k8s.deployment.name', + + /** + * The UID of the StatefulSet. + */ + K8S_STATEFULSET_UID: 'k8s.statefulset.uid', + + /** + * The name of the StatefulSet. + */ + K8S_STATEFULSET_NAME: 'k8s.statefulset.name', + + /** + * The UID of the DaemonSet. + */ + K8S_DAEMONSET_UID: 'k8s.daemonset.uid', + + /** + * The name of the DaemonSet. + */ + K8S_DAEMONSET_NAME: 'k8s.daemonset.name', + + /** + * The UID of the Job. + */ + K8S_JOB_UID: 'k8s.job.uid', + + /** + * The name of the Job. + */ + K8S_JOB_NAME: 'k8s.job.name', + + /** + * The UID of the CronJob. + */ + K8S_CRONJOB_UID: 'k8s.cronjob.uid', + + /** + * The name of the CronJob. + */ + K8S_CRONJOB_NAME: 'k8s.cronjob.name', + + /** + * The operating system type. + */ + OS_TYPE: 'os.type', + + /** + * Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. + */ + OS_DESCRIPTION: 'os.description', + + /** + * Process identifier (PID). + */ + PROCESS_PID: 'process.pid', + + /** + * The name of the process executable. On Linux based systems, can be set to the `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_NAME: 'process.executable.name', + + /** + * The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_PATH: 'process.executable.path', + + /** + * The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. + */ + PROCESS_COMMAND: 'process.command', + + /** + * The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. + */ + PROCESS_COMMAND_LINE: 'process.command_line', + + /** + * All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. + */ + PROCESS_COMMAND_ARGS: 'process.command_args', + + /** + * The username of the user that owns the process. + */ + PROCESS_OWNER: 'process.owner', + + /** + * The name of the runtime of this process. For compiled native binaries, this SHOULD be the name of the compiler. + */ + PROCESS_RUNTIME_NAME: 'process.runtime.name', + + /** + * The version of the runtime of this process, as returned by the runtime without modification. + */ + PROCESS_RUNTIME_VERSION: 'process.runtime.version', + + /** + * An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. + */ + PROCESS_RUNTIME_DESCRIPTION: 'process.runtime.description', + + /** + * Logical name of the service. + * + * Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md#process), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`. + */ + SERVICE_NAME: 'service.name', + + /** + * A namespace for `service.name`. + * + * Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. + */ + SERVICE_NAMESPACE: 'service.namespace', + + /** + * The string ID of the service instance. + * + * Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words `service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service). It is preferable for the ID to be persistent and stay the same for the lifetime of the service instance, however it is acceptable that the ID is ephemeral and changes during important lifetime events for the service (e.g. service restarts). If the service has no inherent unique ID that can be used as the value of this attribute it is recommended to generate a random Version 1 or Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use Version 5, see RFC 4122 for more recommendations). + */ + SERVICE_INSTANCE_ID: 'service.instance.id', + + /** + * The version string of the service API or implementation. + */ + SERVICE_VERSION: 'service.version', + + /** + * The name of the telemetry SDK as defined above. + */ + TELEMETRY_SDK_NAME: 'telemetry.sdk.name', + + /** + * The language of the telemetry SDK. + */ + TELEMETRY_SDK_LANGUAGE: 'telemetry.sdk.language', + + /** + * The version string of the telemetry SDK. + */ + TELEMETRY_SDK_VERSION: 'telemetry.sdk.version', + + /** + * The version string of the auto instrumentation agent, if used. + */ + TELEMETRY_AUTO_VERSION: 'telemetry.auto.version', +}; + +// Enum definitions + +export enum CloudProviderValues { + /** Amazon Web Services. */ + AWS = 'aws', + /** Microsoft Azure. */ + AZURE = 'azure', + /** Google Cloud Platform. */ + GCP = 'gcp', +} + +export enum CloudInfrastructureServiceValues { + /** AWS Elastic Compute Cloud. */ + AWS_EC2 = 'aws_ec2', + /** AWS Elastic Container Service. */ + AWS_ECS = 'aws_ecs', + /** AWS Elastic Kubernetes Service. */ + AWS_EKS = 'aws_eks', + /** AWS Lambda. */ + AWS_LAMBDA = 'aws_lambda', + /** AWS Elastic Beanstalk. */ + AWS_ELASTICBEANSTALK = 'aws_elastic_beanstalk', + /** Azure Virtual Machines. */ + AZURE_VM = 'azure_vm', + /** Azure Container Instances. */ + AZURE_CONTAINERINSTANCES = 'azure_container_instances', + /** Azure Kubernetes Service. */ + AZURE_AKS = 'azure_aks', + /** Azure Functions. */ + AZURE_FUNCTIONS = 'azure_functions', + /** Azure App Service. */ + AZURE_APPSERVICE = 'azure_app_service', + /** Google Cloud Compute Engine (GCE). */ + GCP_COMPUTEENGINE = 'gcp_compute_engine', + /** Google Cloud Run. */ + GCP_CLOUDRUN = 'gcp_cloud_run', + /** Google Cloud Kubernetes Engine (GKE). */ + GCP_KUBERNETESENGINE = 'gcp_kubernetes_engine', + /** Google Cloud Functions (GCF). */ + GCP_CLOUDFUNCTIONS = 'gcp_cloud_functions', + /** Google Cloud App Engine (GAE). */ + GCP_APPENGINE = 'gcp_app_engine', +} + +export enum AwsEcsLaunchtypeValues { + /** ec2. */ + EC2 = 'ec2', + /** fargate. */ + FARGATE = 'fargate', +} + +export enum HostArchValues { + /** AMD64. */ + AMD64 = 'amd64', + /** ARM32. */ + ARM32 = 'arm32', + /** ARM64. */ + ARM64 = 'arm64', + /** Itanium. */ + IA64 = 'ia64', + /** 32-bit PowerPC. */ + PPC32 = 'ppc32', + /** 64-bit PowerPC. */ + PPC64 = 'ppc64', + /** 32-bit x86. */ + X86 = 'x86', +} + +export enum OsTypeValues { + /** Microsoft Windows. */ + WINDOWS = 'WINDOWS', + /** Linux. */ + LINUX = 'LINUX', + /** Apple Darwin. */ + DARWIN = 'DARWIN', + /** FreeBSD. */ + FREEBSD = 'FREEBSD', + /** NetBSD. */ + NETBSD = 'NETBSD', + /** OpenBSD. */ + OPENBSD = 'OPENBSD', + /** DragonFly BSD. */ + DRAGONFLYBSD = 'DRAGONFLYBSD', + /** HP-UX (Hewlett Packard Unix). */ + HPUX = 'HPUX', + /** AIX (Advanced Interactive eXecutive). */ + AIX = 'AIX', + /** Oracle Solaris. */ + SOLARIS = 'SOLARIS', + /** IBM z/OS. */ + ZOS = 'ZOS', +} + +export enum TelemetrySdkLanguageValues { + /** cpp. */ + CPP = 'cpp', + /** dotnet. */ + DOTNET = 'dotnet', + /** erlang. */ + ERLANG = 'erlang', + /** go. */ + GO = 'go', + /** java. */ + JAVA = 'java', + /** nodejs. */ + NODEJS = 'nodejs', + /** php. */ + PHP = 'php', + /** python. */ + PYTHON = 'python', + /** ruby. */ + RUBY = 'ruby', + /** webjs. */ + WEBJS = 'webjs', +} diff --git a/packages/opentelemetry-plugin-grpc-js/src/index.ts b/packages/opentelemetry-semantic-conventions/src/resource/index.ts similarity index 94% rename from packages/opentelemetry-plugin-grpc-js/src/index.ts rename to packages/opentelemetry-semantic-conventions/src/resource/index.ts index 238da8c96f..e3810b5eeb 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export * from './grpcJs'; +export * from './ResourceAttributes'; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts new file mode 100644 index 0000000000..9f756902fd --- /dev/null +++ b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts @@ -0,0 +1,748 @@ +/* + * 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 = { + /** + * An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. + */ + DB_SYSTEM: 'db.system', + + /** + * The connection string used to connect to the database. It is recommended to remove embedded credentials. + */ + DB_CONNECTION_STRING: 'db.connection_string', + + /** + * Username for accessing the database. + */ + DB_USER: 'db.user', + + /** + * The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect. + */ + DB_JDBC_DRIVER_CLASSNAME: 'db.jdbc.driver_classname', + + /** + * If no [tech-specific attribute](#call-level-attributes-for-specific-technologies) is defined, this attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). + * + * Note: In some SQL databases, the database name to be used is called "schema name". + */ + DB_NAME: 'db.name', + + /** + * The database statement being executed. + * + * Note: The value may be sanitized to exclude sensitive information. + */ + DB_STATEMENT: 'db.statement', + + /** + * The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword. + * + * Note: When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted. + */ + DB_OPERATION: 'db.operation', + + /** + * The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. + * + * Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer required (but still recommended if non-standard). + */ + DB_MSSQL_INSTANCE_NAME: 'db.mssql.instance_name', + + /** + * The name of the keyspace being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_CASSANDRA_KEYSPACE: 'db.cassandra.keyspace', + + /** + * The fetch size used for paging, i.e. how many rows will be returned at once. + */ + DB_CASSANDRA_PAGE_SIZE: 'db.cassandra.page_size', + + /** + * The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). + */ + DB_CASSANDRA_CONSISTENCY_LEVEL: 'db.cassandra.consistency_level', + + /** + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: This mirrors the db.sql.table attribute but references cassandra rather than sql. It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_CASSANDRA_TABLE: 'db.cassandra.table', + + /** + * Whether or not the query is idempotent. + */ + DB_CASSANDRA_IDEMPOTENCE: 'db.cassandra.idempotence', + + /** + * The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. + */ + DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT: + 'db.cassandra.speculative_execution_count', + + /** + * The ID of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_ID: 'db.cassandra.coordinator.id', + + /** + * The data center of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_DC: 'db.cassandra.coordinator.dc', + + /** + * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_HBASE_NAMESPACE: 'db.hbase.namespace', + + /** + * The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute. + */ + DB_REDIS_DATABASE_INDEX: 'db.redis.database_index', + + /** + * The collection being accessed within the database stated in `db.name`. + */ + DB_MONGODB_COLLECTION: 'db.mongodb.collection', + + /** + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_SQL_TABLE: 'db.sql.table', + + /** + * The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. + */ + EXCEPTION_TYPE: 'exception.type', + + /** + * The exception message. + */ + EXCEPTION_MESSAGE: 'exception.message', + + /** + * A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. + */ + EXCEPTION_STACKTRACE: 'exception.stacktrace', + + /** + * SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span. + * + * Note: An exception is considered to have escaped (or left) the scope of a span, +if that span is ended while the exception is still logically "in flight". +This may be actually "in flight" in some languages (e.g. if the exception +is passed to a Context manager's `__exit__` method in Python) but will +usually be caught at the point of recording the exception in most languages. + +It is usually not possible to determine at the point where an exception is thrown +whether it will escape the scope of a span. +However, it is trivial to know that an exception +will escape, if one checks for an active exception just before ending the span, +as done in the [example above](#exception-end-example). + +It follows that an exception may still escape the scope of the span +even if the `exception.escaped` attribute was not set or set to false, +since the event might have been recorded at a time where it was not +clear whether the exception will escape. + */ + EXCEPTION_ESCAPED: 'exception.escaped', + + /** + * Type of the trigger on which the function is executed. + */ + FAAS_TRIGGER: 'faas.trigger', + + /** + * The execution ID of the current function execution. + */ + FAAS_EXECUTION: 'faas.execution', + + /** + * The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. + */ + FAAS_DOCUMENT_COLLECTION: 'faas.document.collection', + + /** + * Describes the type of the operation that was performed on the data. + */ + FAAS_DOCUMENT_OPERATION: 'faas.document.operation', + + /** + * A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_DOCUMENT_TIME: 'faas.document.time', + + /** + * The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. + */ + FAAS_DOCUMENT_NAME: 'faas.document.name', + + /** + * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_TIME: 'faas.time', + + /** + * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + */ + FAAS_CRON: 'faas.cron', + + /** + * A boolean that is true if the serverless function is executed for the first time (aka cold-start). + */ + FAAS_COLDSTART: 'faas.coldstart', + + /** + * The name of the invoked function. + * + * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. + */ + FAAS_INVOKED_NAME: 'faas.invoked_name', + + /** + * The cloud provider of the invoked function. + * + * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. + */ + FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', + + /** + * The cloud region of the invoked function. + * + * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. + */ + FAAS_INVOKED_REGION: 'faas.invoked_region', + + /** + * Transport protocol used. See note below. + */ + NET_TRANSPORT: 'net.transport', + + /** + * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). + */ + NET_PEER_IP: 'net.peer.ip', + + /** + * Remote port number. + */ + NET_PEER_PORT: 'net.peer.port', + + /** + * Remote hostname or similar, see note below. + */ + NET_PEER_NAME: 'net.peer.name', + + /** + * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. + */ + NET_HOST_IP: 'net.host.ip', + + /** + * Like `net.peer.port` but for the host port. + */ + NET_HOST_PORT: 'net.host.port', + + /** + * Local hostname or similar, see note below. + */ + NET_HOST_NAME: 'net.host.name', + + /** + * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. + */ + PEER_SERVICE: 'peer.service', + + /** + * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. + */ + ENDUSER_ID: 'enduser.id', + + /** + * Actual/assumed role the client is making the request under extracted from token or application security context. + */ + ENDUSER_ROLE: 'enduser.role', + + /** + * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + */ + ENDUSER_SCOPE: 'enduser.scope', + + /** + * Current "managed" thread ID (as opposed to OS thread ID). + */ + THREAD_ID: 'thread.id', + + /** + * Current thread name. + */ + THREAD_NAME: 'thread.name', + + /** + * The method or function name, or equivalent (usually rightmost part of the code unit's name). + */ + CODE_FUNCTION: 'code.function', + + /** + * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + */ + CODE_NAMESPACE: 'code.namespace', + + /** + * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + */ + CODE_FILEPATH: 'code.filepath', + + /** + * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + */ + CODE_LINENO: 'code.lineno', + + /** + * HTTP request method. + */ + HTTP_METHOD: 'http.method', + + /** + * Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless. + * + * Note: `http.url` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case the attribute's value should be `https://www.example.com/`. + */ + HTTP_URL: 'http.url', + + /** + * The full request target as passed in a HTTP request line or equivalent. + */ + HTTP_TARGET: 'http.target', + + /** + * The value of the [HTTP host header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is empty or not present, this attribute should be the same. + */ + HTTP_HOST: 'http.host', + + /** + * The URI scheme identifying the used protocol. + */ + HTTP_SCHEME: 'http.scheme', + + /** + * [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + */ + HTTP_STATUS_CODE: 'http.status_code', + + /** + * Kind of HTTP protocol used. + * + * Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. + */ + HTTP_FLAVOR: 'http.flavor', + + /** + * Value of the [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the client. + */ + HTTP_USER_AGENT: 'http.user_agent', + + /** + * 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://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_REQUEST_CONTENT_LENGTH: 'http.request_content_length', + + /** + * The size of the uncompressed request payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: + 'http.request_content_length_uncompressed', + + /** + * 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://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_RESPONSE_CONTENT_LENGTH: 'http.response_content_length', + + /** + * The size of the uncompressed response payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: + 'http.response_content_length_uncompressed', + + /** + * The primary server name of the matched virtual host. This should be obtained via configuration. If no such configuration can be obtained, this attribute MUST NOT be set ( `net.host.name` should be used instead). + * + * Note: `http.url` is usually not readily available on the server side but would have to be assembled in a cumbersome and sometimes lossy process from other information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus preferred to supply the raw data that is available. + */ + HTTP_SERVER_NAME: 'http.server_name', + + /** + * The matched route (path template). + */ + HTTP_ROUTE: 'http.route', + + /** + * The IP address of the original client behind all proxies, if known (e.g. from [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). + * + * Note: This is not necessarily the same as `net.peer.ip`, which would identify the network-level peer, which may be a proxy. + */ + HTTP_CLIENT_IP: 'http.client_ip', + + /** + * A string identifying the messaging system. + */ + MESSAGING_SYSTEM: 'messaging.system', + + /** + * The message destination name. This might be equal to the span name but is required nevertheless. + */ + MESSAGING_DESTINATION: 'messaging.destination', + + /** + * The kind of message destination. + */ + MESSAGING_DESTINATION_KIND: 'messaging.destination_kind', + + /** + * A boolean that is true if the message destination is temporary. + */ + MESSAGING_TEMP_DESTINATION: 'messaging.temp_destination', + + /** + * The name of the transport protocol. + */ + MESSAGING_PROTOCOL: 'messaging.protocol', + + /** + * The version of the transport protocol. + */ + MESSAGING_PROTOCOL_VERSION: 'messaging.protocol_version', + + /** + * Connection string. + */ + MESSAGING_URL: 'messaging.url', + + /** + * A value used by the messaging system as an identifier for the message, represented as a string. + */ + MESSAGING_MESSAGE_ID: 'messaging.message_id', + + /** + * The [conversation ID](#conversations) identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". + */ + MESSAGING_CONVERSATION_ID: 'messaging.conversation_id', + + /** + * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. + */ + MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: 'messaging.message_payload_size_bytes', + + /** + * The compressed size of the message payload in bytes. + */ + MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: + 'messaging.message_payload_compressed_size_bytes', + + /** + * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. + */ + MESSAGING_OPERATION: 'messaging.operation', + + /** + * Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message_id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. + * + * Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. + */ + MESSAGING_KAFKA_MESSAGE_KEY: 'messaging.kafka.message_key', + + /** + * Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. + */ + MESSAGING_KAFKA_CONSUMER_GROUP: 'messaging.kafka.consumer_group', + + /** + * Client Id for the Consumer or Producer that is handling the message. + */ + MESSAGING_KAFKA_CLIENT_ID: 'messaging.kafka.client_id', + + /** + * Partition the message is sent to. + */ + MESSAGING_KAFKA_PARTITION: 'messaging.kafka.partition', + + /** + * A boolean that is true if the message is a tombstone. + */ + MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone', + + /** + * A string identifying the remoting system. + */ + RPC_SYSTEM: 'rpc.system', + + /** + * The full name of the service being called, including its package name, if applicable. + */ + RPC_SERVICE: 'rpc.service', + + /** + * The name of the method being called, must be equal to the $method part in the span name. + */ + RPC_METHOD: 'rpc.method', + + /** + * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. + */ + RPC_GRPC_STATUS_CODE: 'rpc.grpc.status_code', +}; + +// Enum definitions + +export enum DbSystemValues { + /** Some other SQL database. Fallback only. See notes. */ + OTHER_SQL = 'other_sql', + /** Microsoft SQL Server. */ + MSSQL = 'mssql', + /** MySQL. */ + MYSQL = 'mysql', + /** Oracle Database. */ + ORACLE = 'oracle', + /** IBM Db2. */ + DB2 = 'db2', + /** PostgreSQL. */ + POSTGRESQL = 'postgresql', + /** Amazon Redshift. */ + REDSHIFT = 'redshift', + /** Apache Hive. */ + HIVE = 'hive', + /** Cloudscape. */ + CLOUDSCAPE = 'cloudscape', + /** HyperSQL DataBase. */ + HSQLDB = 'hsqldb', + /** Progress Database. */ + PROGRESS = 'progress', + /** SAP MaxDB. */ + MAXDB = 'maxdb', + /** SAP HANA. */ + HANADB = 'hanadb', + /** Ingres. */ + INGRES = 'ingres', + /** FirstSQL. */ + FIRSTSQL = 'firstsql', + /** EnterpriseDB. */ + EDB = 'edb', + /** InterSystems Caché. */ + CACHE = 'cache', + /** Adabas (Adaptable Database System). */ + ADABAS = 'adabas', + /** Firebird. */ + FIREBIRD = 'firebird', + /** Apache Derby. */ + DERBY = 'derby', + /** FileMaker. */ + FILEMAKER = 'filemaker', + /** Informix. */ + INFORMIX = 'informix', + /** InstantDB. */ + INSTANTDB = 'instantdb', + /** InterBase. */ + INTERBASE = 'interbase', + /** MariaDB. */ + MARIADB = 'mariadb', + /** Netezza. */ + NETEZZA = 'netezza', + /** Pervasive PSQL. */ + PERVASIVE = 'pervasive', + /** PointBase. */ + POINTBASE = 'pointbase', + /** SQLite. */ + SQLITE = 'sqlite', + /** Sybase. */ + SYBASE = 'sybase', + /** Teradata. */ + TERADATA = 'teradata', + /** Vertica. */ + VERTICA = 'vertica', + /** H2. */ + H2 = 'h2', + /** ColdFusion IMQ. */ + COLDFUSION = 'coldfusion', + /** Apache Cassandra. */ + CASSANDRA = 'cassandra', + /** Apache HBase. */ + HBASE = 'hbase', + /** MongoDB. */ + MONGODB = 'mongodb', + /** Redis. */ + REDIS = 'redis', + /** Couchbase. */ + COUCHBASE = 'couchbase', + /** CouchDB. */ + COUCHDB = 'couchdb', + /** Microsoft Azure Cosmos DB. */ + COSMOSDB = 'cosmosdb', + /** Amazon DynamoDB. */ + DYNAMODB = 'dynamodb', + /** Neo4j. */ + NEO4J = 'neo4j', + /** Apache Geode. */ + GEODE = 'geode', + /** Elasticsearch. */ + ELASTICSEARCH = 'elasticsearch', +} + +export enum NetTransportValues {} + +export enum DbCassandraConsistencyLevelValues { + /** ALL. */ + ALL = 'ALL', + /** EACH_QUORUM. */ + EACH_QUORUM = 'EACH_QUORUM', + /** QUORUM. */ + QUORUM = 'QUORUM', + /** LOCAL_QUORUM. */ + LOCAL_QUORUM = 'LOCAL_QUORUM', + /** ONE. */ + ONE = 'ONE', + /** TWO. */ + TWO = 'TWO', + /** THREE. */ + THREE = 'THREE', + /** LOCAL_ONE. */ + LOCAL_ONE = 'LOCAL_ONE', + /** ANY. */ + ANY = 'ANY', + /** SERIAL. */ + SERIAL = 'SERIAL', + /** LOCAL_SERIAL. */ + LOCAL_SERIAL = 'LOCAL_SERIAL', +} + +export enum FaasTriggerValues { + /** A response to some data source operation such as a database or filesystem read/write. */ + DATASOURCE = 'datasource', + /** To provide an answer to an inbound HTTP request. */ + HTTP = 'http', + /** A function is set to be executed when messages are sent to a messaging system. */ + PUBSUB = 'pubsub', + /** A function is scheduled to be executed regularly. */ + TIMER = 'timer', + /** If none of the others apply. */ + OTHER = 'other', +} + +export enum FaasDocumentOperationValues { + /** When a new object is created. */ + INSERT = 'insert', + /** When an object is modified. */ + EDIT = 'edit', + /** When an object is deleted. */ + DELETE = 'delete', +} + +export enum FaasInvokedProviderValues { + /** Amazon Web Services. */ + AWS = 'aws', + /** Amazon Web Services. */ + AZURE = 'azure', + /** Google Cloud Platform. */ + GCP = 'gcp', +} + +export enum NetTransportValues { + /** IP.TCP. */ + IP_TCP = 'IP.TCP', + /** IP.UDP. */ + IP_UDP = 'IP.UDP', + /** Another IP-based protocol. */ + IP = 'IP', + /** Unix Domain socket. See below. */ + UNIX = 'Unix', + /** Named or anonymous pipe. See note below. */ + PIPE = 'pipe', + /** In-process communication. */ + INPROC = 'inproc', + /** Something else (non IP-based). */ + OTHER = 'other', +} + +export enum HttpFlavorValues { + /** HTTP 1.0. */ + HTTP_1_0 = '1.0', + /** HTTP 1.1. */ + HTTP_1_1 = '1.1', + /** HTTP 2. */ + HTTP_2_0 = '2.0', + /** SPDY protocol. */ + SPDY = 'SPDY', + /** QUIC protocol. */ + QUIC = 'QUIC', +} + +export enum MessagingDestinationKindValues { + /** A message sent to a queue. */ + QUEUE = 'queue', + /** A message sent to a topic. */ + TOPIC = 'topic', +} + +export enum MessagingOperationValues { + /** receive. */ + RECEIVE = 'receive', + /** process. */ + PROCESS = 'process', +} + +export enum NetTransportValues {} + +export enum RpcGrpcStatusCodeValues { + /** OK. */ + OK = 0, + /** CANCELLED. */ + CANCELLED = 1, + /** UNKNOWN. */ + UNKNOWN = 2, + /** INVALID_ARGUMENT. */ + INVALID_ARGUMENT = 3, + /** DEADLINE_EXCEEDED. */ + DEADLINE_EXCEEDED = 4, + /** NOT_FOUND. */ + NOT_FOUND = 5, + /** ALREADY_EXISTS. */ + ALREADY_EXISTS = 6, + /** PERMISSION_DENIED. */ + PERMISSION_DENIED = 7, + /** RESOURCE_EXHAUSTED. */ + RESOURCE_EXHAUSTED = 8, + /** FAILED_PRECONDITION. */ + FAILED_PRECONDITION = 9, + /** ABORTED. */ + ABORTED = 10, + /** OUT_OF_RANGE. */ + OUT_OF_RANGE = 11, + /** UNIMPLEMENTED. */ + UNIMPLEMENTED = 12, + /** INTERNAL. */ + INTERNAL = 13, + /** UNAVAILABLE. */ + UNAVAILABLE = 14, + /** DATA_LOSS. */ + DATA_LOSS = 15, + /** UNAUTHENTICATED. */ + UNAUTHENTICATED = 16, +} diff --git a/packages/opentelemetry-semantic-conventions/src/trace/database.ts b/packages/opentelemetry-semantic-conventions/src/trace/database.ts deleted file mode 100644 index b8e4bfa6de..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/database.ts +++ /dev/null @@ -1,156 +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. - */ - -/** - * Database attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md - */ -export const DatabaseAttribute = { - // Connection-level attributes - - /** - * An identifier for the database management system (DBMS) product being used. - * - * @remarks - * Required. - */ - DB_SYSTEM: 'db.system', - - /** - * The connection string used to connect to the database. - * It is recommended to remove embedded credentials. - * - * @remarks - * Optional. - */ - DB_CONNECTION_STRING: 'db.connection_string', - - /** - * Username for accessing the database, e.g., "readonly_user" or "reporting_user". - * - * @remarks - * Optional. - */ - DB_USER: 'db.user', - - // Please see ./general.ts for NET_PEER_NAME, NET_PEER_IP, NET_PEER_PORT, NET_TRANSPORT - - // Call-level attributes - - /** - * If no [tech-specific attribute](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md#call-level-attributes-for-specific-technologies) - * is defined in the list below, - * this attribute is used to report the name of the database being accessed. - * For commands that switch the database,this should be set to the - * target database (even if the command fails). - * - * @remarks - * Required if applicable and no more specific attribute is defined. - */ - DB_NAME: 'db.name', - - /** - * The database statement being executed. - * Note that the value may be sanitized to exclude sensitive information. - * E.g., for db.system="other_sql", "SELECT * FROM wuser_table"; - * for db.system="redis", "SET mykey 'WuValue'". - * - * @remarks - * Required if applicable. - */ - DB_STATEMENT: 'db.statement', - - /** - * The name of the operation being executed, - * e.g. the MongoDB command name such as findAndModify. - * While it would semantically make sense to set this, - * e.g., to an SQL keyword like SELECT or INSERT, - * it is not recommended to attempt any client-side parsing of - * db.statement just to get this property (the back end can do that if required). - * - * @remarks - * Required if db.statement is not applicable. - */ - DB_OPERATION: 'db.operation', - - // Connection-level attributes for specific technologies - - /** - * The instance name connecting to. - * This name is used to determine the port of a named instance. - * - * @remarks - * If setting a `db.mssql.instance_name`, - * `net.peer.port` is no longer required (but still recommended if non-standard) - */ - DB_MSSSQL_INSTANCE_NAME: 'db.mssql.instance_name', - - /** - * The fully-qualified class name of the Java Database Connectivity (JDBC) driver used to connect, - * e.g., "org.postgresql.Driver" or "com.microsoft.sqlserver.jdbc.SQLServerDriver". - * - * @remarks - * Optional. - */ - DB_JDBC_DRIVER_CLASSNAME: 'db.jdbc.driver_classname', - - // Call-level attributes for specific technologies - - /** - * The name of the keyspace being accessed. To be used instead of the generic db.name attribute. - * - * @remarks - * Required. - */ - DB_CASSANDRA_KEYSPACE: 'db.cassandra.keyspace', - - /** - * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. - * To be used instead of the generic db.name attribute. - * - * @remarks - * Required. - */ - DB_HBASE_NAMESPACE: 'db.hbase.namespace', - - /** - * The index of the database being accessed as used in the [SELECT command](https://redis.io/commands/select), - * provided as an integer. To be used instead of the generic db.name attribute. - * - * @remarks - * Required if other than the default database (0). - */ - DB_REDIS_DATABASE_INDEX: 'db.redis.database_index', - - /** - * The collection being accessed within the database stated in db.name. - * - * @remarks - * Required. - */ - DB_MONGODB_COLLECTION: 'db.mongodb.collection', - - // Not in spec. - - /** Deprecated. Not in spec. */ - DB_TYPE: 'db.type', - - /** Deprecated. Not in spec. */ - DB_INSTANCE: 'db.instance', - - /** Deprecated. Not in spec. */ - DB_URL: 'db.url', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/faas.ts b/packages/opentelemetry-semantic-conventions/src/trace/faas.ts deleted file mode 100644 index aff39d78c9..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/faas.ts +++ /dev/null @@ -1,170 +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. - */ - -/** - * FaaS (Function as a Service) attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/faas.md - */ -export const FaasAttribute = { - // General attributes - - /** - * The name of the function being executed. - * - * @remarks - * Required. - */ - FAAS_NAME: 'faas.name', - - /** - * The unique ID of the function being executed. - * For example, in AWS Lambda this field corresponds to the ARN value, in GCP to the URI of the resource, and in Azure to the FunctionDirectory field. - * - * @remarks - * Required. - */ - FAAS_ID: 'faas.id', - - /** - * The version string of the function being executed. - * - * @remarks - * Optional. - */ - FAAS_VERSION: 'faas.version', - - /** - * The execution environment ID as a string. - * - * @remarks - * Optional. - */ - FAAS_INSTANCE: 'faas.instance', - - /** - * Type of the trigger on which the function is executed. - * Possible values: datasource, http, pubsub, timer, other - * - * @remarks - * Required if applicable. - */ - FAAS_TRIGGER: 'faas.trigger', - - /** - * The execution ID of the current function execution. - * - * @remarks - * Optional. - */ - FAAS_EXECUTION: 'faas.execution', - - // Incoming Invocations - - /** - * A boolean that is true if the serverless function is executed for the first time (aka cold-start). - * - * @remarks - * Optional. - */ - FAAS_COLD_START: 'faas.coldstart', - - // Outgoing Invocations - // This section describes outgoing FaaS invocations as they are reported by a client calling a FaaS instance. - - /** - * The name of the invoked function. - * SHOULD be equal to the faas.name resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_NAME: 'faas.invoked_name', - - /** - * The cloud provider of the invoked function. - * SHOULD be equal to the cloud.provider resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', - - /** - * The cloud region of the invoked function. - * SHOULD be equal to the cloud.region resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_REGION: 'faas.invoked_region', - - // Datesource Trigger - - /** - * The name of the source on which the triggering operation was performed. - * For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_COLLECTION: 'faas.document.collection', - - /** - * Describes the type of the operation that was performed on the data. - * MUST be one of the following or, if none of the listed values apply, a custom value: insert, edit, delete - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_OPERATION: 'faas.document.operation', - - /** - * A string containing the time when the data was accessed in the ISO 8601 format expressed in UTC. - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_TIME: 'faas.document.time', - - /** - * The document name/table subjected to the operation. - * For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. - * - * @remarks - * Optional. - */ - FAAS_DOC_NAME: 'faas.document.name', - - // Timer Trigger - - /** - * A string containing the function invocation time in the ISO 8601 format expressed in UTC. - * Example: "2020-01-23T13:47:06Z" - * - * @remarks - * Required if applicable. - */ - FAAS_TIME: 'faas.time', - - /** - * A string containing the schedule period as Cron Expression. - * Example: "0/5 * * * ? *" - * - * @remarks - * Optional. - */ - FAAS_CRON: 'faas.cron', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/general.ts b/packages/opentelemetry-semantic-conventions/src/trace/general.ts deleted file mode 100644 index 6fa64be3aa..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/general.ts +++ /dev/null @@ -1,41 +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. - */ - -/** - * General purpose networking attributes defined by the OpenTelemetry Semantic Conventions Specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md - */ -export const GeneralAttribute = { - NET_PEER_IP: 'net.peer.ip', - NET_PEER_ADDRESS: 'net.peer.address', - NET_PEER_HOSTNAME: 'net.peer.host', - NET_PEER_PORT: 'net.peer.port', - NET_PEER_NAME: 'net.peer.name', - NET_PEER_IPV4: 'net.peer.ipv4', - NET_PEER_IPV6: 'net.peer.ipv6', - NET_PEER_SERVICE: 'net.peer.service', - NET_HOST_IP: 'net.host.ip', - NET_HOST_PORT: 'net.host.port', - NET_HOST_NAME: 'net.host.name', - NET_TRANSPORT: 'net.transport', - - // These are used as potential values to NET_TRANSPORT - IP_TCP: 'IP.TCP', - IP_UDP: 'IP.UDP', - INPROC: 'inproc', - PIPE: 'pipe', - UNIX: 'Unix', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/http.ts b/packages/opentelemetry-semantic-conventions/src/trace/http.ts deleted file mode 100644 index 04618c5fd9..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/http.ts +++ /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. - */ -export const HttpAttribute = { - HTTP_HOST: 'http.host', - HTTP_METHOD: 'http.method', - HTTP_TARGET: 'http.target', - HTTP_ROUTE: 'http.route', - HTTP_URL: 'http.url', - HTTP_STATUS_CODE: 'http.status_code', - HTTP_STATUS_TEXT: 'http.status_text', - HTTP_FLAVOR: 'http.flavor', - HTTP_SERVER_NAME: 'http.server_name', - HTTP_CLIENT_IP: 'http.client_ip', - HTTP_SCHEME: 'http.scheme', - HTTP_RESPONSE_CONTENT_LENGTH: 'http.response_content_length', - HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: - 'http.response_content_length_uncompressed', - HTTP_REQUEST_CONTENT_LENGTH: 'http.request_content_length', - HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: - 'http.request_content_length_uncompressed', - - // NOT ON OFFICIAL SPEC - HTTP_ERROR_NAME: 'http.error_name', - HTTP_ERROR_MESSAGE: 'http.error_message', - HTTP_USER_AGENT: 'http.user_agent', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/index.ts b/packages/opentelemetry-semantic-conventions/src/trace/index.ts index 20b5ebd5d5..aa5c102427 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/index.ts @@ -13,12 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export * from './database'; -export * from './exception'; -export * from './general'; -export * from './http'; -export * from './os'; -export * from './rpc'; -export * from './faas'; -export * from './messaging'; +export * from './SemanticAttributes'; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts b/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts deleted file mode 100644 index c5266a60a2..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts +++ /dev/null @@ -1,151 +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. - */ - -/** - * messaging attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md - */ -export const MessagingAttribute = { - /** - * A string identifying the messaging system. - * example: kafka, rabbitmq, sqs, sns - * - * @remarks - * Required. - */ - MESSAGING_SYSTEM: 'messaging.system', - - /** - * The message destination name. This might be equal to the span name but is required nevertheless. - * example: MyQueue, MyTopic - * - * @remarks - * Required. - */ - MESSAGING_DESTINATION: 'messaging.destination', - - /** - * The kind of message destination - * allowed values: queue, topic, - * - * @remarks - * Required only if the message destination is either a queue or topic. - */ - MESSAGING_DESTINATION_KIND: 'messaging.destination_kind', - - /** - * A boolean that is true if the message destination is temporary - * - * @remarks - * Conditional If missing, it is assumed to be false. - */ - MESSAGING_TEMP_DESTINATION: 'messaging.temp_destination', - - /** - * The kind of message destination - * allowed values: queue, topic, - * - * @remarks - * Required only if the message destination is either a queue or topic. - */ - MESSAGING_PROTOCOL: 'messaging.protocol', - - /** - * The version of the transport protocol. - * - * @remarks - * Optional. - */ - MESSAGING_PROTOCOL_VERSION: 'messaging.protocol_version', - - /** - * Connection string. - * example: https://queue.amazonaws.com/80398EXAMPLE/MyQueue - * - * @remarks - * Optional. - */ - MESSAGING_URL: 'messaging.url', - - /** - * A value used by the messaging system as an identifier for the message, represented as a string. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_ID: 'messaging.message_id', - - /** - * The conversation ID identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". - * - * @remarks - * Optional. - */ - MESSAGING_CONVERSATION_ID: 'messaging.conversation_id', - - /** - * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. - * Should be number. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: 'messaging.message_payload_size_bytes', - - /** - * The compressed size of the message payload in bytes. - * Should be number. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: - 'messaging.message_payload_compressed_size_bytes', - - /** - * For message consumers only. - * allowed values: receive, process, - * - * @remarks - * Optional. - */ - MESSAGING_OPERATION: 'messaging.operation', - - // System specific attributes - MESSAGING_RABBITMQ_ROUTING_KEY: 'messaging.rabbitmq.routing_key', - MESSAGING_KAFKA_MESSAGE_KEY: 'messaging.kafka.message_key', - MESSAGING_KAFKA_CONSUMER_GROUP: 'messaging.kafka.consumer_group', - MESSAGING_KAFKA_CLIENT_ID: 'messaging.kafka.client_id', - MESSAGING_KAFKA_PARTITION: 'messaging.kafka.partition', - MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone', -}; - -export const MessagingOperationName = { - /** - * A message is sent to a destination by a message producer/client. - */ - SEND: 'send', - - /** - * A message is received from a destination by a message consumer/server. - */ - RECEIVE: 'receive', - - /** - * A message that was previously received from a destination is processed by a message consumer/server. - */ - PROCESS: 'process', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/os.ts b/packages/opentelemetry-semantic-conventions/src/trace/os.ts deleted file mode 100644 index 1ffa94ff56..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/os.ts +++ /dev/null @@ -1,63 +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. - */ - -/** - * The operating system (OS) on which the process represented by this resource is running. - * - * In case of virtualized environments, this is the operating system as it - * is observed by the process, i.e., the virtualized guest rather than the - * underlying host. - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/os.md - */ -export const OperatingSystem = { - /** - * The operating system type. - * This should be set to one of {@link OperatingSystemValues} - * E.g., "WINDOWS" - * - * @remarks - * Required. - */ - TYPE: 'os.type', - /** - * Human readable (not intended to be parsed) OS version information. - * E.g., "Microsoft Windows [Version 10.0.18363.778]" - * - * @remarks - * Required if applicable. - */ - DESCRIPTION: 'os.description', -}; - -/** - * {@link OperatingSystem.TYPE} SHOULD be set to one of the values - * listed below. - * If none of the listed values apply, a custom value best describing - * the family the operating system belongs to CAN be used. - */ -export const OperatingSystemValues = { - WINDOWS: 'WINDOWS', - LINUX: 'LINUX', - DARWIN: 'DARWIN', - FREEBSD: 'FREEBSD', - NETBSD: 'NETBSD', - OPENBSD: 'OPENBSD', - DRAGONFLYBSD: 'DRAGONFLYBSD', - HPUX: 'HPUX', - AIX: 'AIX', - SOLARIS: 'SOLARIS', - ZOS: 'ZOS', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts b/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts deleted file mode 100644 index ec23d93bf3..0000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts +++ /dev/null @@ -1,47 +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. - */ -export const RpcAttribute = { - /** - * A string identifying the remoting system. - * - * @remarks - * Required - */ - RPC_SYSTEM: 'rpc.system', - - /** - * The full name of the service being called, including its package name, if applicable. - * - * @remarks - * Not required, but recommended - */ - RPC_SERVICE: 'rpc.service', - - /** - * The name of the method being called, must be equal to the $method part in the span name. - * - * @remarks - * Not required, but recommended - */ - RPC_METHOD: 'rpc.method', - - // GRPC (no spec) - GRPC_KIND: 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD: 'grpc.method', - GRPC_STATUS_CODE: 'grpc.status_code', - GRPC_ERROR_NAME: 'grpc.error_name', - GRPC_ERROR_MESSAGE: 'grpc.error_message', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/version.ts b/packages/opentelemetry-semantic-conventions/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-semantic-conventions/src/version.ts +++ b/packages/opentelemetry-semantic-conventions/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-shim-opentracing/package.json b/packages/opentelemetry-shim-opentracing/package.json index 298988bf85..795f837a56 100644 --- a/packages/opentelemetry-shim-opentracing/package.json +++ b/packages/opentelemetry-shim-opentracing/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/shim-opentracing", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTracing to OpenTelemetry shim", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -38,9 +38,10 @@ "access": "public" }, "devDependencies": { - "@opentelemetry/tracing": "^0.18.0", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/tracing": "0.18.2", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "codecov": "3.8.1", "gts": "3.1.0", "mocha": "7.2.0", @@ -50,11 +51,13 @@ "ts-node": "9.1.1", "tslint-consistent-codestyle": "1.16.0", "tslint-microsoft-contrib": "6.2.0", - "typescript": "4.2.3" + "typescript": "4.2.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", + "@opentelemetry/core": "0.18.2", "opentracing": "^0.14.4" } } diff --git a/packages/opentelemetry-shim-opentracing/src/version.ts b/packages/opentelemetry-shim-opentracing/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-shim-opentracing/src/version.ts +++ b/packages/opentelemetry-shim-opentracing/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-tracing/package.json b/packages/opentelemetry-tracing/package.json index 9f6e73596a..ffb92f9759 100644 --- a/packages/opentelemetry-tracing/package.json +++ b/packages/opentelemetry-tracing/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/tracing", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Tracing", "main": "build/src/index.js", "browser": { @@ -48,9 +48,10 @@ "access": "public" }, "devDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0", "@types/lodash.merge": "4.6.6", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "codecov": "3.8.1", @@ -61,22 +62,24 @@ "karma-coverage-istanbul-reporter": "3.0.3", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", + "@opentelemetry/core": "0.18.2", + "@opentelemetry/resources": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2", "lodash.merge": "^4.6.2" } } diff --git a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts index 5eee7bea22..b5db060271 100644 --- a/packages/opentelemetry-tracing/src/BasicTracerProvider.ts +++ b/packages/opentelemetry-tracing/src/BasicTracerProvider.ts @@ -19,11 +19,14 @@ import { trace, context, propagation, + TextMapPropagator, + diag, } from '@opentelemetry/api'; import { CompositePropagator, - HttpBaggage, HttpTraceContext, + HttpBaggage, + getEnv, } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { SpanProcessor, Tracer } from '.'; @@ -32,10 +35,21 @@ import { MultiSpanProcessor } from './MultiSpanProcessor'; import { NoopSpanProcessor } from './NoopSpanProcessor'; import { SDKRegistrationConfig, TracerConfig } from './types'; import merge = require('lodash.merge'); + +export type PROPAGATOR_FACTORY = () => TextMapPropagator; + /** * This class represents a basic tracer provider which platform libraries can extend */ export class BasicTracerProvider implements TracerProvider { + protected static readonly _registeredPropagators = new Map< + string, + PROPAGATOR_FACTORY + >([ + ['tracecontext', () => new HttpTraceContext()], + ['baggage', () => new HttpBaggage()], + ]); + private readonly _config: TracerConfig; private readonly _registeredSpanProcessors: SpanProcessor[] = []; private readonly _tracers: Map = new Map(); @@ -86,9 +100,7 @@ export class BasicTracerProvider implements TracerProvider { register(config: SDKRegistrationConfig = {}) { trace.setGlobalTracerProvider(this); if (config.propagator === undefined) { - config.propagator = new CompositePropagator({ - propagators: [new HttpBaggage(), new HttpTraceContext()], - }); + config.propagator = this._buildPropagatorFromEnv(); } if (config.contextManager) { @@ -103,4 +115,43 @@ export class BasicTracerProvider implements TracerProvider { shutdown() { return this.activeSpanProcessor.shutdown(); } + + protected _getPropagator(name: string): TextMapPropagator | undefined { + return BasicTracerProvider._registeredPropagators.get(name)?.(); + } + + protected _buildPropagatorFromEnv(): TextMapPropagator | undefined { + // per spec, propagators from env must be deduplicated + const uniquePropagatorNames = [...new Set(getEnv().OTEL_PROPAGATORS)]; + + const propagators = uniquePropagatorNames.map(name => { + const propagator = this._getPropagator(name); + if (!propagator) { + diag.warn( + `Propagator "${name}" requested through environment variable is unavailable.` + ); + } + + return propagator; + }); + const validPropagators = propagators.reduce( + (list, item) => { + if (item) { + list.push(item); + } + return list; + }, + [] + ); + + if (validPropagators.length === 0) { + return; + } else if (uniquePropagatorNames.length === 1) { + return validPropagators[0]; + } else { + return new CompositePropagator({ + propagators: validPropagators, + }); + } + } } diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index ea6ebce182..bfaf77b20b 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -24,15 +24,13 @@ import { timeInputToHrTime, } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; -import { - ExceptionAttribute, - ExceptionEventName, -} from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { ReadableSpan } from './export/ReadableSpan'; import { Tracer } from './Tracer'; import { SpanProcessor } from './SpanProcessor'; import { TraceParams } from './types'; import { SpanAttributeValue, Context } from '@opentelemetry/api'; +import { ExceptionEventName } from './enums'; /** * This class represents a span. @@ -190,25 +188,27 @@ export class Span implements api.Span, ReadableSpan { recordException(exception: api.Exception, time: api.TimeInput = hrTime()) { const attributes: api.SpanAttributes = {}; if (typeof exception === 'string') { - attributes[ExceptionAttribute.MESSAGE] = exception; + attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception; } else if (exception) { if (exception.code) { - attributes[ExceptionAttribute.TYPE] = exception.code; + attributes[ + SemanticAttributes.EXCEPTION_TYPE + ] = exception.code.toString(); } else if (exception.name) { - attributes[ExceptionAttribute.TYPE] = exception.name; + attributes[SemanticAttributes.EXCEPTION_TYPE] = exception.name; } if (exception.message) { - attributes[ExceptionAttribute.MESSAGE] = exception.message; + attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception.message; } if (exception.stack) { - attributes[ExceptionAttribute.STACKTRACE] = exception.stack; + attributes[SemanticAttributes.EXCEPTION_STACKTRACE] = exception.stack; } } // these are minimum requirements from spec if ( - attributes[ExceptionAttribute.TYPE] || - attributes[ExceptionAttribute.MESSAGE] + attributes[SemanticAttributes.EXCEPTION_TYPE] || + attributes[SemanticAttributes.EXCEPTION_MESSAGE] ) { this.addEvent(ExceptionEventName, attributes as api.SpanAttributes, time); } else { diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/index.ts b/packages/opentelemetry-tracing/src/enums.ts similarity index 89% rename from packages/opentelemetry-plugin-grpc-js/src/client/index.ts rename to packages/opentelemetry-tracing/src/enums.ts index 549cc29a6a..af2909dc68 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/client/index.ts +++ b/packages/opentelemetry-tracing/src/enums.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export * from './loadPackageDefinition'; -export * from './patchClient'; +// Event name definitions +export const ExceptionEventName = 'exception'; diff --git a/packages/opentelemetry-tracing/src/version.ts b/packages/opentelemetry-tracing/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-tracing/src/version.ts +++ b/packages/opentelemetry-tracing/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts b/packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts index bfef16e093..0f64753a24 100644 --- a/packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts +++ b/packages/opentelemetry-tracing/test/BasicTracerProvider.test.ts @@ -22,7 +22,14 @@ import { setSpan, setSpanContext, getSpan, + TextMapPropagator, + TextMapSetter, + Context, + TextMapGetter, + propagation, + diag, } from '@opentelemetry/api'; +import { CompositePropagator } from '@opentelemetry/core'; import { AlwaysOnSampler, AlwaysOffSampler, @@ -64,9 +71,9 @@ describe('BasicTracerProvider', () => { it('should construct an instance with default trace params', () => { const tracer = new BasicTracerProvider({}).getTracer('default'); assert.deepStrictEqual(tracer.getActiveTraceParams(), { - numberOfAttributesPerSpan: 1000, - numberOfEventsPerSpan: 1000, - numberOfLinksPerSpan: 1000, + numberOfAttributesPerSpan: 128, + numberOfEventsPerSpan: 128, + numberOfLinksPerSpan: 128, }); }); @@ -78,8 +85,8 @@ describe('BasicTracerProvider', () => { }).getTracer('default'); assert.deepStrictEqual(tracer.getActiveTraceParams(), { numberOfAttributesPerSpan: 100, - numberOfEventsPerSpan: 1000, - numberOfLinksPerSpan: 1000, + numberOfEventsPerSpan: 128, + numberOfLinksPerSpan: 128, }); }); @@ -90,9 +97,9 @@ describe('BasicTracerProvider', () => { }, }).getTracer('default'); assert.deepStrictEqual(tracer.getActiveTraceParams(), { - numberOfAttributesPerSpan: 1000, + numberOfAttributesPerSpan: 128, numberOfEventsPerSpan: 300, - numberOfLinksPerSpan: 1000, + numberOfLinksPerSpan: 128, }); }); @@ -103,8 +110,8 @@ describe('BasicTracerProvider', () => { }, }).getTracer('default'); assert.deepStrictEqual(tracer.getActiveTraceParams(), { - numberOfAttributesPerSpan: 1000, - numberOfEventsPerSpan: 1000, + numberOfAttributesPerSpan: 128, + numberOfEventsPerSpan: 128, numberOfLinksPerSpan: 10, }); }); @@ -115,6 +122,99 @@ describe('BasicTracerProvider', () => { }); }); + describe('.register()', () => { + const envSource = (typeof window !== 'undefined' + ? window + : process.env) as any; + + describe('propagator', () => { + class DummyPropagator implements TextMapPropagator { + inject( + context: Context, + carrier: any, + setter: TextMapSetter + ): void { + throw new Error('Method not implemented.'); + } + extract( + context: Context, + carrier: any, + getter: TextMapGetter + ): Context { + throw new Error('Method not implemented.'); + } + fields(): string[] { + throw new Error('Method not implemented.'); + } + } + + let setGlobalPropagatorStub: sinon.SinonSpy< + [TextMapPropagator], + TextMapPropagator + >; + let originalPropagators: string | number | undefined | string[]; + beforeEach(() => { + setGlobalPropagatorStub = sinon.spy(propagation, 'setGlobalPropagator'); + originalPropagators = envSource.OTEL_PROPAGATORS; + }); + + afterEach(() => { + setGlobalPropagatorStub.restore(); + + // otherwise we may assign 'undefined' (a string) + if (originalPropagators !== undefined) { + envSource.OTEL_PROPAGATORS = originalPropagators; + } else { + delete envSource.OTEL_PROPAGATORS; + } + }); + + it('should be set to a given value if it it provided', () => { + const provider = new BasicTracerProvider(); + provider.register({ + propagator: new DummyPropagator(), + }); + assert.ok( + setGlobalPropagatorStub.calledOnceWithExactly( + sinon.match.instanceOf(DummyPropagator) + ) + ); + }); + + it('should be composite if 2 or more propagators provided in an environment variable', () => { + const provider = new BasicTracerProvider(); + provider.register(); + + assert.ok( + setGlobalPropagatorStub.calledOnceWithExactly( + sinon.match.instanceOf(CompositePropagator) + ) + ); + assert.deepStrictEqual(setGlobalPropagatorStub.args[0][0].fields(), [ + 'traceparent', + 'tracestate', + 'baggage', + ]); + }); + + it('warns if there is no propagator registered with a given name', () => { + const warnStub = sinon.spy(diag, 'warn'); + + envSource.OTEL_PROPAGATORS = 'missing-propagator'; + const provider = new BasicTracerProvider({}); + provider.register(); + + assert.ok( + warnStub.calledOnceWithExactly( + 'Propagator "missing-propagator" requested through environment variable is unavailable.' + ) + ); + + warnStub.restore(); + }); + }); + }); + describe('.startSpan()', () => { it('should start a span with name only', () => { const tracer = new BasicTracerProvider().getTracer('default'); diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 5597223e02..23dcacd639 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -29,7 +29,7 @@ import { hrTimeToMilliseconds, hrTimeToNanoseconds, } from '@opentelemetry/core'; -import { ExceptionAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import { BasicTracerProvider, Span, SpanProcessor } from '../src'; @@ -692,10 +692,11 @@ describe('Span', () => { assert.ok(event.attributes); - const type = event.attributes[ExceptionAttribute.TYPE]; - const message = event.attributes[ExceptionAttribute.MESSAGE]; + const type = event.attributes[SemanticAttributes.EXCEPTION_TYPE]; + const message = + event.attributes[SemanticAttributes.EXCEPTION_MESSAGE]; const stacktrace = String( - event.attributes[ExceptionAttribute.STACKTRACE] + event.attributes[SemanticAttributes.EXCEPTION_STACKTRACE] ); assert.strictEqual(type, 'Error'); assert.strictEqual(message, 'boom'); @@ -719,5 +720,23 @@ describe('Span', () => { assert.deepStrictEqual(event.time, [0, 123]); }); }); + + describe('when exception code is numeric', () => { + it('should record an exception with string value', () => { + const span = new Span( + tracer, + ROOT_CONTEXT, + name, + spanContext, + SpanKind.CLIENT + ); + assert.strictEqual(span.events.length, 0); + span.recordException({ code: 12 }); + const event = span.events[0]; + assert.deepStrictEqual(event.attributes, { + [SemanticAttributes.EXCEPTION_TYPE]: '12', + }); + }); + }); }); }); diff --git a/packages/opentelemetry-web/package.json b/packages/opentelemetry-web/package.json index 5605c3c497..e479c9ec62 100644 --- a/packages/opentelemetry-web/package.json +++ b/packages/opentelemetry-web/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/web", - "version": "0.18.0", + "version": "0.18.2", "description": "OpenTelemetry Web Tracer", "main": "build/src/index.js", "types": "build/src/index.d.ts", @@ -41,13 +41,14 @@ "access": "public" }, "devDependencies": { - "@babel/core": "7.13.10", - "@opentelemetry/context-zone": "^0.18.0", - "@opentelemetry/propagator-b3": "^0.18.0", - "@opentelemetry/resources": "^0.18.0", + "@babel/core": "7.13.15", + "@opentelemetry/api": "^1.0.0-rc.0", + "@opentelemetry/context-zone": "0.18.2", + "@opentelemetry/propagator-b3": "0.18.2", + "@opentelemetry/resources": "0.18.2", "@types/jquery": "3.5.5", "@types/mocha": "8.2.2", - "@types/node": "14.14.35", + "@types/node": "14.14.37", "@types/sinon": "9.0.11", "@types/webpack-env": "1.16.0", "babel-loader": "8.2.2", @@ -60,23 +61,25 @@ "karma-jquery": "0.2.4", "karma-mocha": "2.0.1", "karma-spec-reporter": "0.0.32", - "karma-webpack": "4.0.2", + "karma-webpack": "5.0.0", "mocha": "7.2.0", "nyc": "15.1.0", "rimraf": "3.0.2", - "sinon": "9.2.4", - "ts-loader": "8.0.18", + "sinon": "10.0.0", + "ts-loader": "8.1.0", "ts-mocha": "8.0.0", "ts-node": "9.1.1", - "typescript": "4.2.3", + "typescript": "4.2.4", "webpack": "4.46.0", - "webpack-cli": "4.5.0", + "webpack-cli": "4.6.0", "webpack-merge": "5.7.3" }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0-rc.0" + }, "dependencies": { - "@opentelemetry/api": "^1.0.0-rc.0", - "@opentelemetry/core": "^0.18.0", - "@opentelemetry/semantic-conventions": "^0.18.0", - "@opentelemetry/tracing": "^0.18.0" + "@opentelemetry/core": "0.18.2", + "@opentelemetry/semantic-conventions": "0.18.2", + "@opentelemetry/tracing": "0.18.2" } } diff --git a/packages/opentelemetry-web/src/WebTracerProvider.ts b/packages/opentelemetry-web/src/WebTracerProvider.ts index c541b2b75d..0212f132af 100644 --- a/packages/opentelemetry-web/src/WebTracerProvider.ts +++ b/packages/opentelemetry-web/src/WebTracerProvider.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; import { BasicTracerProvider, SDKRegistrationConfig, @@ -25,12 +24,7 @@ import { StackContextManager } from './StackContextManager'; /** * WebTracerConfig provides an interface for configuring a Web Tracer. */ -export interface WebTracerConfig extends TracerConfig { - /** - * plugins to be used with tracer, they will be enabled automatically - */ - plugins?: unknown[]; -} +export type WebTracerConfig = TracerConfig; /** * This class represents a web tracer with {@link StackContextManager} @@ -41,12 +35,6 @@ export class WebTracerProvider extends BasicTracerProvider { * @param config Web Tracer config */ constructor(config: WebTracerConfig = {}) { - if (typeof config.plugins !== 'undefined') { - diag.warn( - 'plugins option was removed, please use' + - ' "registerInstrumentations" to load plugins' - ); - } super(config); if ((config as SDKRegistrationConfig).contextManager) { diff --git a/packages/opentelemetry-web/src/utils.ts b/packages/opentelemetry-web/src/utils.ts index 8c6a1a0e86..af9d15c9d3 100644 --- a/packages/opentelemetry-web/src/utils.ts +++ b/packages/opentelemetry-web/src/utils.ts @@ -26,7 +26,7 @@ import { timeInputToHrTime, urlMatches, } from '@opentelemetry/core'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; // Used to normalize relative URLs let a: HTMLAnchorElement | undefined; @@ -90,7 +90,7 @@ export function addSpanNetworkEvents( const contentLength = resource[PTN.ENCODED_BODY_SIZE]; if (contentLength !== undefined) { span.setAttribute( - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, contentLength ); } diff --git a/packages/opentelemetry-web/src/version.ts b/packages/opentelemetry-web/src/version.ts index 6fdb996caa..6fff6ffcee 100644 --- a/packages/opentelemetry-web/src/version.ts +++ b/packages/opentelemetry-web/src/version.ts @@ -15,4 +15,4 @@ */ // this is autogenerated file, see scripts/version-update.js -export const VERSION = '0.18.0'; +export const VERSION = '0.18.2'; diff --git a/packages/opentelemetry-web/test/WebTracerProvider.test.ts b/packages/opentelemetry-web/test/WebTracerProvider.test.ts index 5bde1bd183..e13d9b9ac5 100644 --- a/packages/opentelemetry-web/test/WebTracerProvider.test.ts +++ b/packages/opentelemetry-web/test/WebTracerProvider.test.ts @@ -14,14 +14,12 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; import { context, getSpan, setSpan, ContextManager } from '@opentelemetry/api'; import { ZoneContextManager } from '@opentelemetry/context-zone'; import { B3Propagator } from '@opentelemetry/propagator-b3'; import { Resource, TELEMETRY_SDK_RESOURCE } from '@opentelemetry/resources'; import { Span, Tracer } from '@opentelemetry/tracing'; import * as assert from 'assert'; -import * as sinon from 'sinon'; import { WebTracerConfig } from '../src'; import { WebTracerProvider } from '../src/WebTracerProvider'; @@ -48,21 +46,6 @@ describe('WebTracerProvider', () => { assert.ok(tracer instanceof Tracer); }); - it('should show warning when plugins are defined', () => { - const dummyPlugin1 = {}; - const spyWarn = sinon.spy(diag, 'warn'); - - const plugins = [dummyPlugin1]; - - const options = { plugins }; - new WebTracerProvider(options); - - assert.strictEqual( - spyWarn.args[0][0], - 'plugins option was removed, please use "registerInstrumentations" to load plugins' - ); - }); - it('should work without default context manager', () => { assert.doesNotThrow(() => { new WebTracerProvider({}); diff --git a/packages/template/package.json b/packages/template/package.json index 91c3c00ca0..0216914e69 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -1,6 +1,6 @@ { "name": "@opentelemetry/template", - "version": "0.18.0", + "version": "0.18.2", "private": true, "publishConfig": { "access": "restricted" @@ -57,8 +57,8 @@ "README.md" ], "devDependencies": { - "@types/node": "14.14.35", - "typescript": "4.2.3" + "@types/node": "14.14.37", + "typescript": "4.2.4" }, "Add these to devDependencies for testing": { "@types/mocha": "8.2.0", diff --git a/scripts/peer-api-check.js b/scripts/peer-api-check.js new file mode 100644 index 0000000000..6e359e0473 --- /dev/null +++ b/scripts/peer-api-check.js @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +const appRoot = process.cwd(); + +const packageJsonUrl = path.resolve(`${appRoot}/package.json`); +const pjson = require(packageJsonUrl); + +if (pjson.dependencies && pjson.dependencies["@opentelemetry/api"]) + throw new Error(`Package ${pjson.name} depends on API but it should be a peer dependency`); + +const peerVersion = pjson.peerDependencies && pjson.peerDependencies["@opentelemetry/api"] +const devVersion = pjson.devDependencies && pjson.devDependencies["@opentelemetry/api"] +if (peerVersion) { + if (peerVersion !== devVersion) { + throw new Error(`Package ${pjson.name} depends on peer API version ${peerVersion} but version ${devVersion} in development`); + } + console.log(`${pjson.name} OK`); +} diff --git a/scripts/semconv/.gitignore b/scripts/semconv/.gitignore new file mode 100644 index 0000000000..a93b221beb --- /dev/null +++ b/scripts/semconv/.gitignore @@ -0,0 +1 @@ +opentelemetry-specification/ diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh new file mode 100755 index 0000000000..589537c269 --- /dev/null +++ b/scripts/semconv/generate.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="${SCRIPT_DIR}/../../" + +# freeze the spec version to make SpanAttributess generation reproducible +SPEC_VERSION=v1.1.0 +GENERATOR_VERSION=0.2.1 + +cd ${SCRIPT_DIR} + +rm -rf opentelemetry-specification || true +mkdir opentelemetry-specification +cd opentelemetry-specification + +git init +git remote add origin https://github.com/open-telemetry/opentelemetry-specification.git +git fetch origin "$SPEC_VERSION" --depth=1 +git reset --hard FETCH_HEAD +cd ${SCRIPT_DIR} + +docker run --rm \ + -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/trace:/source \ + -v ${SCRIPT_DIR}/templates:/templates \ + -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/trace/:/output \ + otel/semconvgen:${GENERATOR_VERSION} \ + -f /source \ + code \ + --template /templates/SemanticAttributes.ts.j2 \ + --output /output/SemanticAttributes.ts \ + -Dclass=SemanticAttributes + +docker run --rm \ + -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/resource:/source \ + -v ${SCRIPT_DIR}/templates:/templates \ + -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/resource/:/output \ + otel/semconvgen:${GENERATOR_VERSION} \ + -f /source \ + code \ + --template /templates/SemanticAttributes.ts.j2 \ + --output /output/ResourceAttributes.ts \ + -Dclass=ResourceAttributes + +# Run the automatic linting fixing task to ensure it will pass eslint +cd "$ROOT_DIR/packages/opentelemetry-semantic-conventions" + +npm run lint:fix + +cd "$ROOT_DIR" diff --git a/scripts/semconv/templates/SemanticAttributes.ts.j2 b/scripts/semconv/templates/SemanticAttributes.ts.j2 new file mode 100644 index 0000000000..f2ee675302 --- /dev/null +++ b/scripts/semconv/templates/SemanticAttributes.ts.j2 @@ -0,0 +1,60 @@ +/* + * 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. + */ + + +{%- macro print_value(type, value) -%} + {{ "\"" if type == "string"}}{{value}}{{ "\"" if type == "string"}} +{%- endmacro %} +{%- macro upFirst(text) -%} + {{ text[0]|upper}}{{text[1:] }} +{%- endmacro %} +{%- macro lowerFirst(text) -%} + {{ text[0]|lower}}{{text[1:] }} +{%- endmacro %} + +// DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates/{{template}} +export const {{class}} = { + {%- for attribute in attributes if attribute.is_local and not attribute.ref %} + + /** + * {% filter escape %}{{attribute.brief | to_doc_brief}}.{% endfilter %} + {%- if attribute.note %} + * + * Note: {% filter escape %}{{attribute.note | to_doc_brief}}.{% endfilter %} + {%- endif %} + {%- if attribute.deprecated %} + * + * @deprecated {{attribute.deprecated | to_doc_brief}}. + {%- endif %} + */ + {{attribute.fqn | to_const_name}}: '{{attribute.fqn}}', + {%- endfor %} +} + +// Enum definitions +{%- for attribute in attributes if attribute.is_local %} +{%- if attribute.is_enum %} +{%- set class_name = attribute.fqn | to_camelcase(True) ~ "Values" %} +{%- set type = attribute.attr_type.enum_type %} + +export enum {{class_name}} { + {%- for member in attribute.attr_type.members if attribute.is_local and not attribute.ref %} + /** {% filter escape %}{{member.brief | to_doc_brief}}.{% endfilter %} */ + {{ member.member_id | to_const_name }} = {{ print_value(type, member.value) }}, + {%- endfor %} +} +{% endif %} +{%- endfor %} diff --git a/tsconfig.json b/tsconfig.json index 485642f6e7..44e36c8025 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,9 +44,6 @@ { "path": "packages/opentelemetry-exporter-zipkin" }, - { - "path": "packages/opentelemetry-grpc-utils" - }, { "path": "packages/opentelemetry-instrumentation-fetch" }, @@ -69,19 +66,10 @@ "path": "packages/opentelemetry-node" }, { - "path": "packages/opentelemetry-plugin-grpc-js" - }, - { - "path": "packages/opentelemetry-plugin-grpc" - }, - { - "path": "packages/opentelemetry-plugin-http" - }, - { - "path": "packages/opentelemetry-plugin-https" + "path": "packages/opentelemetry-propagator-b3" }, { - "path": "packages/opentelemetry-propagator-b3" + "path": "packages/opentelemetry-propagator-jaeger" }, { "path": "packages/opentelemetry-resource-detector-aws" diff --git a/website_docs/_index.md b/website_docs/_index.md new file mode 100644 index 0000000000..8124c87cea --- /dev/null +++ b/website_docs/_index.md @@ -0,0 +1,26 @@ +--- +title: "Javascript" +weight: 20 +description: > + + A language-specific implementation of OpenTelemetry in JavaScript (for Node.JS & the browser). +--- + +This page contains an introduction to OpenTelemetry in JavaScript. This guide +will walk you through installation and instrumentation and show you how to +export data. + +## Status and Releases + +| Tracing | Metrics | +| ------- | ------- | +| Beta | Alpha | + +You can find release information [here](https://github.com/open-telemetry/opentelemetry-js/releases) + +## Further Reading + +- [OpenTelemetry for JavaScript on GitHub](https://github.com/open-telemetry/opentelemetry-js) +- [Getting Started](https://github.com/open-telemetry/opentelemetry-js/blob/main/getting-started/README.md) +- [API Documentation](https://open-telemetry.github.io/opentelemetry-js) +- [Getting In Touch (Gitter)](https://gitter.im/open-telemetry/opentelemetry-node) diff --git a/website_docs/getting_started/_index.md b/website_docs/getting_started/_index.md new file mode 100644 index 0000000000..3ab3b1ca5f --- /dev/null +++ b/website_docs/getting_started/_index.md @@ -0,0 +1,9 @@ +--- +title: "Getting Started" +weight: 1 +--- +These two guides for Node.JS and the browser use simple examples in javascript to get you started with OpenTelemetry. Both will show you the following steps: + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) diff --git a/website_docs/getting_started/browser.md b/website_docs/getting_started/browser.md new file mode 100644 index 0000000000..876a776f12 --- /dev/null +++ b/website_docs/getting_started/browser.md @@ -0,0 +1,188 @@ +--- +title: "Browser" +weight: 2 +--- + +This guide uses the example application in HTML & javascript provided below, but the steps to instrument your own application should be broadly the same. Here is an overview of what we will be doing. + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) + +This is a very simple guide, if you'd like to see more complex examples go to [examples/tracer-web](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web) + +Copy the following file into an empty directory and call it `index.html`. + +```html + + + + + Document Load Plugin Example + + + + + + + Example of using Web Tracer with document load plugin with console exporter and collector exporter + + +``` + +## Installation + +To create traces in the browser, you will need `@opentelemetry/web`, and the plugin `@opentelemetry/plugin-document-load`: + +```shell +npm install @opentelemetry/web @opentelemetry/plugin-document-load +``` + +In the following we will use parcel as web application bundler, but you can of course also use any other build tool: + +```shell +npm install -g parcel +``` + +## Initialization and Configuration + +Create a empty file called `document-load.js` and add the following code to your html right before the body end tag: + +```html + +``` + +We will add some code that will trace the document load timings and output those as OpenTelemetry Spans. + +## Creating a Tracer Provider + +Add the following code to the `document-load.js` to create a tracer provider, which brings the plugin to trace document load: + +```javascript + // This is necessary for "parcel" to work OOTB. It is not needed for other build tools. +import 'regenerator-runtime/runtime' +import { LogLevel } from "@opentelemetry/core"; +import { WebTracerProvider } from '@opentelemetry/web'; +import { DocumentLoad } from '@opentelemetry/plugin-document-load'; + +// Minimum required setup - supports only synchronous operations +const provider = new WebTracerProvider({ + plugins: [ + new DocumentLoad() + ] +}); +provider.register(); +``` + +Run `parcel index.html` and open the development webserver (e.g. at `http://localhost:1234`) to see if your code works. + +There will be no output of traces yet, for this we need to add an exporter + +## Creating a Console Exporter + +To export traces, modify `document-load.js` so that it matches the following code snippet: + +```javascript + // This is necessary for "parcel" to work OOTB. It is not needed for other build tools. +import 'regenerator-runtime/runtime' +import { LogLevel } from "@opentelemetry/core"; +import { WebTracerProvider } from '@opentelemetry/web'; +import { DocumentLoad } from '@opentelemetry/plugin-document-load'; +import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing'; + +// Minimum required setup - supports only synchronous operations +const provider = new WebTracerProvider({ + plugins: [ + new DocumentLoad() + ] +}); +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())) +provider.register(); +``` + +Now, rebuild your application and open the browser again. In the console of the developer toolbar you should see some traces being exporterd: + +```json +{ + "traceId": "ab42124a3c573678d4d8b21ba52df3bf", + "parentId": "cfb565047957cb0d", + "name": "documentFetch", + "id": "5123fc802ffb5255", + "kind": 0, + "timestamp": 1606814247811266, + "duration": 9390, + "attributes": { + "component": "document-load", + "http.response_content_length": 905 + }, + "status": { + "code": 0 + }, + "events": [ + { + "name": "fetchStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "domainLookupStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "domainLookupEnd", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "connectStart", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "connectEnd", + "time": [ + 1606814247, + 811266158 + ] + }, + { + "name": "requestStart", + "time": [ + 1606814247, + 819101158 + ] + }, + { + "name": "responseStart", + "time": [ + 1606814247, + 819791158 + ] + }, + { + "name": "responseEnd", + "time": [ + 1606814247, + 820656158 + ] + } + ] +} +``` diff --git a/website_docs/getting_started/nodejs.md b/website_docs/getting_started/nodejs.md new file mode 100644 index 0000000000..9bd45090c9 --- /dev/null +++ b/website_docs/getting_started/nodejs.md @@ -0,0 +1,288 @@ +--- +title: "Node.JS" +weight: 2 +--- + +This guide uses the example application in node.js provided below, but the steps to instrument your own application should be broadly the same. Here is an overview of what we will be doing. + +- Install the required OpenTelemetry libraries +- Initialize a global [tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer) +- Initialize and register a [span exporter](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-exporter) + +Copy the following file into an empty directory and call it `app.js`. + +```javascript +"use strict"; + +const PORT = process.env.PORT || "8080"; + +const express = require("express"); + +const app = express(); + +app.get("/", (req, res) => { + res.send("Hello World"); +}); + +app.listen(parseInt(PORT, 10), () => { + console.log(`Listening for requests on http://localhost:${PORT}`); +}); +``` + +Run `npm install express` to have all dependencies available. + +## Installation + +To create traces on NodeJS, you will need `@opentelemetry/node`, `@opentelemetry/core`, and any plugins required by your application such as gRPC, or HTTP. If you are using the example application, you will need to install `@opentelemetry/plugin-http`, `@opentelemetry/plugin-https` and `@opentelemetry/plugin-express`. + +```sh +$ npm install \ + @opentelemetry/core \ + @opentelemetry/node \ + @opentelemetry/plugin-http \ + @opentelemetry/plugin-https \ + @opentelemetry/plugin-express \ + @opentelemetry/metrics \ + @opentelemetry/tracing +``` + +## Initialization and Configuration + +All tracing initialization should happen before your application’s code runs. The easiest way to do this is to initialize tracing in a separate file that is required using node’s `-r` option before application code runs. + +## Creating a Tracer Provider + +Create a file named `tracing.js` and add the following code to create a tracer provider: + +```javascript +'use strict'; + +const { LogLevel } = require("@opentelemetry/core"); +const { NodeTracerProvider } = require("@opentelemetry/node"); + +const provider = new NodeTracerProvider({ + logLevel: LogLevel.ERROR +}); + +provider.register(); +``` + +If you run your application now with `node -r ./tracing.js app.js`, your application will create and propagate traces over HTTP. If an already instrumented service that supports [Trace Context](https://www.w3.org/TR/trace-context/) headers calls your application using HTTP, and you call another application using HTTP, the Trace Context headers will be correctly propagated. + +If you wish to see a completed trace, however, there is one more step. You must register an exporter. + +## Creating a Metric Provider + +In order to create and monitor metrics, we will need a `Meter`. In OpenTelemetry, a `Meter` is the mechanism used to create and manage metrics, labels, and metric exporters. + +Create a file named `monitoring.js` and add the following code: + +```javascript +'use strict'; + +const { MeterProvider } = require('@opentelemetry/metrics'); + +const meter = new MeterProvider().getMeter('your-meter-name'); +``` + +Now, you can require this file from your application code and use the `Meter` to create and manage metrics. The simplest of these metrics is a counter. Let's create and export from our `monitoring.js` file a middleware function that express can use to count all requests by route. Modify the `monitoring.js` file so that it looks like this: + +```javascript +'use strict'; + +const { MeterProvider } = require('@opentelemetry/metrics'); + +const meter = new MeterProvider().getMeter('your-meter-name'); + +const requestCount = meter.createCounter("requests", { + description: "Count all incoming requests" +}); + +const boundInstruments = new Map(); + +module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + + boundInstruments.get(req.path).add(1); + next(); + }; +}; +``` + +Now let's import and use this middleware in our application code: + +```javascript +const { countAllRequests } = require("./monitoring"); +const app = express(); +app.use(countAllRequests()); +``` + +Now, when we make requests (e.g. `curl http://localhost:8080`) to our service our meter will count all requests. + +**Note**: Creating a new `labelSet` and `binding` on every request is not ideal as creating the `labelSet` can often be an expensive operation. This is why instruments are created and stored in a `Map` according to the route key. + +## Creating a Console Exporter + +To export traces, modify `tracing.js` so that it matches the following code snippet: + +```javascript +'use strict'; + +const { LogLevel } = require("@opentelemetry/core"); +const { NodeTracerProvider } = require("@opentelemetry/node"); +const { SimpleSpanProcessor, ConsoleSpanExporter } = require("@opentelemetry/tracing"); + +const provider = new NodeTracerProvider({ + logLevel: LogLevel.ERROR +}); + +provider.register(); + +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +console.log("tracing initialized"); +``` + +To export metrics, modify `monitoring.js` so that it matches the following code snippet: + +```javascript +'use strict'; + +const { MeterProvider, ConsoleMetricExporter } = require('@opentelemetry/metrics'); + +const exporter = new ConsoleMetricExporter() + +const meter = new MeterProvider({ + exporter, + interval: 5000 +}).getMeter('your-meter-name'); + +const requestCount = meter.createCounter("requests", { + description: "Count all incoming requests" +}); + +const boundInstruments = new Map(); + +module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + + boundInstruments.get(req.path).add(1); + next(); + }; +}; +``` + +Now, restart your application and add some load, you will see traces & metrics printed to your console: + +```javascript +{ + traceId: 'f27805526b1c74293bbc9345cd48ff3b', + parentId: 'd6bdf2a18df04ef0', + name: 'middleware - query', + id: '36335b81de12cc4a', + kind: 0, + timestamp: 1603789083744612, + duration: 365, + attributes: { + component: 'express', + 'express.name': 'query', + 'express.type': 'middleware' + }, + status: { code: 0 }, + events: [] +} +{ + name: 'requests', + description: 'Count all incoming requests', + unit: '1', + metricKind: 0, + valueType: 1 +} +{ route: '/' } +value: 1 +``` + +If you'd like to write those traces and spanes to Zipkin or Prometheus follow the [complete guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/getting-started/README.md). + +## Quick Start + +To have everything up and running in a few seconds, create an empty directory and create the following files: + +- package.json + + ```json + { + "dependencies": { + "@opentelemetry/core": "^0.12.0", + "@opentelemetry/metrics": "^0.12.0", + "@opentelemetry/node": "^0.12.0", + "@opentelemetry/plugin-express": "^0.10.0", + "@opentelemetry/plugin-http": "^0.12.0", + "@opentelemetry/plugin-https": "^0.12.0", + "express": "^4.17.1" + } + } + ``` + +- app.js + + ```javascript + "use strict"; + const PORT = process.env.PORT || "8080"; + const express = require("express"); + const app = express(); + const { countAllRequests } = require("./monitoring"); + app.use(countAllRequests()); + app.get("/", (req, res) => { res.send("Hello World"); }); + app.listen(parseInt(PORT, 10), () => { console.log(`Listening for requests on http://localhost:${PORT}`); }); + ``` + +- tracing.js + + ```javascript + 'use strict'; + const { LogLevel } = require("@opentelemetry/core"); + const { NodeTracerProvider } = require("@opentelemetry/node"); + const { SimpleSpanProcessor, ConsoleSpanExporter } = require("@opentelemetry/tracing"); + const provider = new NodeTracerProvider({ logLevel: LogLevel.ERROR }); + provider.register(); + provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + console.log("tracing initialized"); + ``` + +- monitoring.js + + ```javascript + 'use strict'; + const { MeterProvider, ConsoleMetricExporter } = require('@opentelemetry/metrics'); + const exporter = new ConsoleMetricExporter() + const meter = new MeterProvider({ + exporter, + interval: 5000 + }).getMeter('your-meter-name'); + const requestCount = meter.createCounter("requests", { description: "Count all incoming requests" }); + const boundInstruments = new Map(); + module.exports.countAllRequests = () => { + return (req, res, next) => { + if (!boundInstruments.has(req.path)) { + const labels = { route: req.path }; + const boundCounter = requestCount.bind(labels); + boundInstruments.set(req.path, boundCounter); + } + boundInstruments.get(req.path).add(1); + next(); + }; + }; + ``` + +Run `npm install` and `node -r ./tracing.js app.js` and add some load to the app, e.g. `curl http://localhost:8080` diff --git a/website_docs/instrumentation.md b/website_docs/instrumentation.md new file mode 100644 index 0000000000..91d21bd6cc --- /dev/null +++ b/website_docs/instrumentation.md @@ -0,0 +1,152 @@ +--- +title: "Instrumentation" +weight: 3 +--- + +This guide will cover creating and annotating spans, creating and annotating metrics, how to pass context, and a guide to automatic instrumentation for JavaScript. This simple example works in the browser as well as with Node.JS + +In the following this guide will use the following sample app: + +```javascript +'use strict'; + +for (let i = 0; i < 10; i += 1) { + doWork(); +} + +function doWork() { + console.log("work...") + // simulate some random work. + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + } +} +``` + +## Creating Spans + +As you have learned in the previous [Getting Started](../getting_started/) guide you need a TracerProvider and an Exporter. Install the dependencies and add them to head of your application code to get started: + +```shell +npm install @opentelemetry/tracing +``` + +```javascript +const { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); + +const provider = new BasicTracerProvider(); + +// Configure span processor to send spans to the exporter +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +provider.register(); +``` + +Next, initialize the OpenTelemetry APIs to use the BasicTracerProvider bindings. +This registers the tracer provider with the OpenTelemetry API as the global tracer provider. +This means when you call API methods like `opentelemetry.trace.getTracer`, they will use this tracer provider. +If you do not register a global tracer provider, instrumentation which calls these methods will receive no-op implementations + +Install the required package and modify your code: + +```shell +npm install @opentelemetry/api +``` + +```javascript +const opentelemetry = require('@opentelemetry/api'); +const tracer = opentelemetry.trace.getTracer('example-basic-tracer-node'); +``` + +Add a first span to the sample application. Modify your code like the following: + +```javascript +// Create a span. A span must be closed. +const parentSpan = tracer.startSpan('main'); +for (let i = 0; i < 10; i += 1) { + doWork(parentSpan); +} +// Be sure to end the span. +parentSpan.end(); + +// flush and close the connection. +exporter.shutdown(); +``` + +Run your application and you will see traces being exported to the console: + +```json +{ + traceId: '833bac85797c7ace581235446c4c769a', + parentId: undefined, + name: 'main', + id: '5c82d9e39d58229e', + kind: 0, + timestamp: 1603790966012813, + duration: 13295, + attributes: {}, + status: { code: 0 }, + events: [] +} +``` + +Add further spans into the `doWork` method: + +```javascript +// Create a span. A span must be closed. +const parentSpan = tracer.startSpan('main'); +for (let i = 0; i < 10; i += 1) { + doWork(parentSpan); +} + +/* ... */ + +function doWork(parent) { + // Start another span. In this example, the main method already started a + // span, so that'll be the parent span, and this will be a child span. + const span = tracer.startSpan('doWork', { + parent, + }); + + // simulate some random work. + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.end(); +} +``` + +Invoking your application once again will give you a list of traces being exported. + +## Attributes + +Attributes can be used to describe your spans. Attributes can be added to a span at any time before the span is finished: + +```javascript +function doWork(parent) { + const span = tracer.startSpan('doWork', { + parent, attributes: { attribute1 : 'value1' } + }); + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.setAttribute('attribute2', 'value2'); + span.end(); +} +``` + +### Semantic Attributes + +There are semantic conventions for spans representing operations in well-known protocols like HTTP or database calls. Semantic conventions for these spans are defined in the specification at [Trace Semantic Conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions). In the simple example of this guide the source code attributes can be used: + +```javascript +function doWork(parent) { + const span = tracer.startSpan('doWork', { + parent, attributes: { 'code.function' : 'doWork' } + }); + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // empty + } + span.setAttribute('code.filepath', __filename); + span.end(); +} +``` diff --git a/website_docs/instrumentation_examples.md b/website_docs/instrumentation_examples.md new file mode 100644 index 0000000000..19b3c5535a --- /dev/null +++ b/website_docs/instrumentation_examples.md @@ -0,0 +1,18 @@ +--- +title: "Instrumentation Examples" +weight: 4 +--- + +Here are Some of the resources for Opentelemetry Instrumentation Examples + +## Community Resources + +### nodejs-opentelemetry-tempo + +Project demonstrating Complete Observability Stack utilizing [Prometheus](https://prometheus.io/), [Loki](https://grafana.com/oss/loki/) (_For distributed logging_), [Tempo](https://grafana.com/oss/tempo/) (_For Distributed tracing, this basically uses Jaeger Internally_), [Grafana](https://grafana.com/grafana/) for **NodeJs** based applications (_With OpenTelemetry auto / manual Instrumentation_) involving microservices with DB interactions. + +Checkout [nodejs-opentelemetry-tempo](https://github.com/mnadeem/nodejs-opentelemetry-tempo) and get started + +````bash +docker-compose up --build +````