From d2e4cd4443b998c82d17e9adcf06da4b6831e88e Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 13:54:10 +0200 Subject: [PATCH 01/10] chore: replacing plugins with instrumentations --- README.md | 40 ++++++++----------- examples/grpc-js/package.json | 2 +- examples/grpc-js/tracer.js | 17 +------- examples/grpc/package.json | 2 +- examples/grpc/tracer.js | 15 ++----- examples/http/package.json | 2 +- examples/http/tracer.js | 14 ++----- examples/https/package.json | 2 +- examples/https/tracer.js | 15 ++----- examples/opentracing-shim/shim.js | 11 ----- getting-started/traced-example/package.json | 5 +-- getting-started/traced-example/tracing.js | 6 +++ getting-started/ts-example/README.md | 32 +++++++++++++-- .../ts-example/traced-example/package.json | 6 +-- .../ts-example/traced-example/tracing.ts | 13 ++++++ 15 files changed, 87 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 3f3d62ab79..1da193aef2 100644 --- a/README.md +++ b/README.md @@ -204,22 +204,20 @@ OpenTelemetry can collect tracing data automatically using instrumentations. Ven ##### 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] @@ -415,11 +413,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 @@ -432,14 +425,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/examples/grpc-js/package.json b/examples/grpc-js/package.json index f554e57a8a..10ebab5f4d 100644 --- a/examples/grpc-js/package.json +++ b/examples/grpc-js/package.json @@ -33,8 +33,8 @@ "@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/plugin-grpc-js": "^0.18.2", "@opentelemetry/tracing": "^0.18.2", "google-protobuf": "^3.9.2" }, 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 036a728414..5320e9c49e 100644 --- a/examples/grpc/package.json +++ b/examples/grpc/package.json @@ -32,8 +32,8 @@ "@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/plugin-grpc": "^0.18.2", "@opentelemetry/tracing": "^0.18.2", "google-protobuf": "^3.9.2", "grpc": "^1.23.3", 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 cb4b13cc11..96b198dbbc 100644 --- a/examples/http/package.json +++ b/examples/http/package.json @@ -32,8 +32,8 @@ "@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/plugin-http": "^0.18.2", "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", 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 bee4904e25..b0b66a73d4 100644 --- a/examples/https/package.json +++ b/examples/https/package.json @@ -33,8 +33,8 @@ "@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/plugin-https": "^0.18.2", "@opentelemetry/tracing": "^0.18.2" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", 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/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/getting-started/traced-example/package.json b/getting-started/traced-example/package.json index 2c1c40fd0c..9470c00075 100644 --- a/getting-started/traced-example/package.json +++ b/getting-started/traced-example/package.json @@ -13,9 +13,8 @@ "@opentelemetry/exporter-zipkin": "^0.18.2", "@opentelemetry/node": "^0.18.2", "@opentelemetry/instrumentation": "^0.18.2", - "@opentelemetry/plugin-express": "^0.14.0", - "@opentelemetry/plugin-http": "^0.18.2", - "@opentelemetry/plugin-https": "^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/traced-example/package.json b/getting-started/ts-example/traced-example/package.json index a6ce2b492b..417b4c66c3 100644 --- a/getting-started/ts-example/traced-example/package.json +++ b/getting-started/ts-example/traced-example/package.json @@ -16,10 +16,10 @@ "dependencies": { "@opentelemetry/core": "^0.18.2", "@opentelemetry/exporter-zipkin": "^0.18.2", + "@opentelemetry/instrumentation": "^0.18.2", "@opentelemetry/node": "^0.18.2", - "@opentelemetry/plugin-express": "^0.14.0", - "@opentelemetry/plugin-http": "^0.18.2", - "@opentelemetry/plugin-https": "^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'); From 4a912c0d1d6bb37feae02650d673c4ac3eda47c2 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 13:56:00 +0200 Subject: [PATCH 02/10] chore: removing grpc and http plugins --- .../.eslintignore | 1 - .../opentelemetry-plugin-grpc-js/.eslintrc.js | 7 - .../opentelemetry-plugin-grpc-js/.npmignore | 4 - packages/opentelemetry-plugin-grpc-js/LICENSE | 201 ----- .../opentelemetry-plugin-grpc-js/README.md | 76 -- .../opentelemetry-plugin-grpc-js/package.json | 75 -- .../src/client/index.ts | 18 - .../src/client/loadPackageDefinition.ts | 70 -- .../src/client/patchClient.ts | 50 -- .../src/client/utils.ts | 251 ------ .../src/grpcJs.ts | 91 -- .../opentelemetry-plugin-grpc-js/src/index.ts | 17 - .../src/server/clientStreamAndUnary.ts | 66 -- .../src/server/index.ts | 17 - .../src/server/patchServer.ts | 218 ----- .../src/server/serverStreamAndBidi.ts | 89 -- .../opentelemetry-plugin-grpc-js/src/types.ts | 64 -- .../opentelemetry-plugin-grpc-js/src/utils.ts | 92 -- .../src/version.ts | 18 - .../test/grpcJs.test.ts | 23 - .../tsconfig.json | 31 - .../opentelemetry-plugin-grpc/.eslintignore | 1 - .../opentelemetry-plugin-grpc/.eslintrc.js | 7 - packages/opentelemetry-plugin-grpc/.npmignore | 4 - packages/opentelemetry-plugin-grpc/LICENSE | 201 ----- packages/opentelemetry-plugin-grpc/README.md | 76 -- .../opentelemetry-plugin-grpc/package.json | 74 -- .../opentelemetry-plugin-grpc/src/grpc.ts | 562 ------------ .../opentelemetry-plugin-grpc/src/index.ts | 17 - .../opentelemetry-plugin-grpc/src/types.ts | 81 -- .../opentelemetry-plugin-grpc/src/utils.ts | 96 -- .../opentelemetry-plugin-grpc/src/version.ts | 18 - .../test/grpc.test.ts | 23 - .../opentelemetry-plugin-grpc/tsconfig.json | 31 - .../opentelemetry-plugin-http/.eslintignore | 1 - .../opentelemetry-plugin-http/.eslintrc.js | 7 - packages/opentelemetry-plugin-http/.npmignore | 4 - packages/opentelemetry-plugin-http/LICENSE | 201 ----- packages/opentelemetry-plugin-http/README.md | 83 -- .../opentelemetry-plugin-http/package.json | 81 -- .../opentelemetry-plugin-http/src/http.ts | 511 ----------- .../opentelemetry-plugin-http/src/index.ts | 19 - .../opentelemetry-plugin-http/src/types.ts | 96 -- .../opentelemetry-plugin-http/src/utils.ts | 497 ----------- .../opentelemetry-plugin-http/src/version.ts | 18 - .../test/fixtures/google.json | 43 - .../test/functionals/http-disable.test.ts | 77 -- .../test/functionals/http-enable.test.ts | 843 ------------------ .../test/functionals/http-package.test.ts | 177 ---- .../test/functionals/utils.test.ts | 451 ---------- .../test/integrations/http-enable.test.ts | 351 -------- .../test/utils/DummyPropagation.ts | 52 -- .../test/utils/assertSpan.ts | 171 ---- .../test/utils/httpRequest.ts | 68 -- .../test/utils/rawRequest.ts | 38 - .../test/utils/utils.ts | 26 - .../opentelemetry-plugin-http/tsconfig.json | 28 - .../opentelemetry-plugin-https/.eslintignore | 1 - .../opentelemetry-plugin-https/.eslintrc.js | 7 - .../opentelemetry-plugin-https/.npmignore | 4 - packages/opentelemetry-plugin-https/LICENSE | 201 ----- packages/opentelemetry-plugin-https/README.md | 78 -- .../opentelemetry-plugin-https/package.json | 81 -- .../opentelemetry-plugin-https/src/https.ts | 139 --- .../opentelemetry-plugin-https/src/index.ts | 17 - .../opentelemetry-plugin-https/src/version.ts | 18 - .../test/fixtures/google.json | 43 - .../test/fixtures/server-cert.pem | 11 - .../test/fixtures/server-key.pem | 15 - .../test/functionals/https-disable.test.ts | 90 -- .../test/functionals/https-enable.test.ts | 657 -------------- .../test/functionals/https-package.test.ts | 189 ---- .../test/integrations/https-enable.test.ts | 355 -------- .../test/utils/DummyPropagation.ts | 52 -- .../test/utils/assertSpan.ts | 126 --- .../test/utils/httpsRequest.ts | 72 -- .../test/utils/utils.ts | 27 - .../opentelemetry-plugin-https/tsconfig.json | 31 - 78 files changed, 8727 deletions(-) delete mode 100644 packages/opentelemetry-plugin-grpc-js/.eslintignore delete mode 100644 packages/opentelemetry-plugin-grpc-js/.eslintrc.js delete mode 100644 packages/opentelemetry-plugin-grpc-js/.npmignore delete mode 100644 packages/opentelemetry-plugin-grpc-js/LICENSE delete mode 100644 packages/opentelemetry-plugin-grpc-js/README.md delete mode 100644 packages/opentelemetry-plugin-grpc-js/package.json delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/client/index.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/client/loadPackageDefinition.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/client/patchClient.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/client/utils.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/grpcJs.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/index.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/index.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/types.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/utils.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/version.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/tsconfig.json delete mode 100644 packages/opentelemetry-plugin-grpc/.eslintignore delete mode 100644 packages/opentelemetry-plugin-grpc/.eslintrc.js delete mode 100644 packages/opentelemetry-plugin-grpc/.npmignore delete mode 100644 packages/opentelemetry-plugin-grpc/LICENSE delete mode 100644 packages/opentelemetry-plugin-grpc/README.md delete mode 100644 packages/opentelemetry-plugin-grpc/package.json delete mode 100644 packages/opentelemetry-plugin-grpc/src/grpc.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/index.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/types.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/utils.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/version.ts delete mode 100644 packages/opentelemetry-plugin-grpc/test/grpc.test.ts delete mode 100644 packages/opentelemetry-plugin-grpc/tsconfig.json delete mode 100644 packages/opentelemetry-plugin-http/.eslintignore delete mode 100644 packages/opentelemetry-plugin-http/.eslintrc.js delete mode 100644 packages/opentelemetry-plugin-http/.npmignore delete mode 100644 packages/opentelemetry-plugin-http/LICENSE delete mode 100644 packages/opentelemetry-plugin-http/README.md delete mode 100644 packages/opentelemetry-plugin-http/package.json delete mode 100644 packages/opentelemetry-plugin-http/src/http.ts delete mode 100644 packages/opentelemetry-plugin-http/src/index.ts delete mode 100644 packages/opentelemetry-plugin-http/src/types.ts delete mode 100644 packages/opentelemetry-plugin-http/src/utils.ts delete mode 100644 packages/opentelemetry-plugin-http/src/version.ts delete mode 100644 packages/opentelemetry-plugin-http/test/fixtures/google.json delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/http-disable.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/utils.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/DummyPropagation.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/assertSpan.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/httpRequest.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/rawRequest.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/utils.ts delete mode 100644 packages/opentelemetry-plugin-http/tsconfig.json delete mode 100644 packages/opentelemetry-plugin-https/.eslintignore delete mode 100644 packages/opentelemetry-plugin-https/.eslintrc.js delete mode 100644 packages/opentelemetry-plugin-https/.npmignore delete mode 100644 packages/opentelemetry-plugin-https/LICENSE delete mode 100644 packages/opentelemetry-plugin-https/README.md delete mode 100644 packages/opentelemetry-plugin-https/package.json delete mode 100644 packages/opentelemetry-plugin-https/src/https.ts delete mode 100644 packages/opentelemetry-plugin-https/src/index.ts delete mode 100644 packages/opentelemetry-plugin-https/src/version.ts delete mode 100644 packages/opentelemetry-plugin-https/test/fixtures/google.json delete mode 100644 packages/opentelemetry-plugin-https/test/fixtures/server-cert.pem delete mode 100644 packages/opentelemetry-plugin-https/test/fixtures/server-key.pem delete mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-disable.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/utils/DummyPropagation.ts delete mode 100644 packages/opentelemetry-plugin-https/test/utils/assertSpan.ts delete mode 100644 packages/opentelemetry-plugin-https/test/utils/httpsRequest.ts delete mode 100644 packages/opentelemetry-plugin-https/test/utils/utils.ts delete mode 100644 packages/opentelemetry-plugin-https/tsconfig.json 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 df809aaa24..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "@opentelemetry/plugin-grpc-js", - "version": "0.18.2", - "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/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^0.18.2", - "@opentelemetry/grpc-utils": "^0.18.2", - "@opentelemetry/node": "^0.18.2", - "@opentelemetry/tracing": "^0.18.2", - "@types/mocha": "8.2.2", - "@types/node": "14.14.37", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, - "dependencies": { - "@opentelemetry/core": "^0.18.2", - "@opentelemetry/semantic-conventions": "^0.18.2", - "shimmer": "1.2.1" - } -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/index.ts b/packages/opentelemetry-plugin-grpc-js/src/client/index.ts deleted file mode 100644 index 549cc29a6a..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/client/index.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. - */ - -export * from './loadPackageDefinition'; -export * from './patchClient'; 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/index.ts b/packages/opentelemetry-plugin-grpc-js/src/index.ts deleted file mode 100644 index 238da8c96f..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/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 './grpcJs'; 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/index.ts b/packages/opentelemetry-plugin-grpc-js/src/server/index.ts deleted file mode 100644 index 99bb1743c1..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/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 './patchServer'; 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/src/version.ts b/packages/opentelemetry-plugin-grpc-js/src/version.ts deleted file mode 100644 index 6fff6ffcee..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/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.2'; diff --git a/packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts b/packages/opentelemetry-plugin-grpc-js/test/grpcJs.test.ts deleted file mode 100644 index 7faf3e8b88..0000000000 --- a/packages/opentelemetry-plugin-grpc-js/test/grpcJs.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, GrpcJsPlugin } from '../src/grpcJs'; -import * as grpc from '@grpc/grpc-js'; - -describe(`#${GrpcJsPlugin.component}`, () => { - runTests(plugin, GrpcJsPlugin.component, grpc, 12346); -}); 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 2ca3dc7097..0000000000 --- a/packages/opentelemetry-plugin-grpc/package.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "@opentelemetry/plugin-grpc", - "version": "0.18.2", - "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/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^0.18.2", - "@opentelemetry/grpc-utils": "^0.18.2", - "@opentelemetry/node": "^0.18.2", - "@opentelemetry/tracing": "^0.18.2", - "@types/mocha": "8.2.2", - "@types/node": "14.14.37", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, - "dependencies": { - "@opentelemetry/core": "^0.18.2", - "@opentelemetry/semantic-conventions": "^0.18.2", - "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 6fff6ffcee..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.2'; 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/package.json b/packages/opentelemetry-plugin-http/package.json deleted file mode 100644 index e7512b6c0b..0000000000 --- a/packages/opentelemetry-plugin-http/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "@opentelemetry/plugin-http", - "version": "0.18.2", - "description": "OpenTelemetry http 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", - "watch": "tsc --build --watch" - }, - "keywords": [ - "opentelemetry", - "http", - "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/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^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.37", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, - "dependencies": { - "@opentelemetry/core": "^0.18.2", - "@opentelemetry/semantic-conventions": "^0.18.2", - "semver": "^7.1.3", - "shimmer": "^1.2.1" - } -} 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 6fff6ffcee..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.2'; 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 803383e5ba..0000000000 --- a/packages/opentelemetry-plugin-https/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "@opentelemetry/plugin-https", - "version": "0.18.2", - "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/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^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.37", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, - "dependencies": { - "@opentelemetry/core": "^0.18.2", - "@opentelemetry/plugin-http": "^0.18.2", - "@opentelemetry/semantic-conventions": "^0.18.2", - "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 6fff6ffcee..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.2'; 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" - } - ] -} From 14bd90a750c42e2bb66e174c6a7f9c67a1a6201f Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 20:35:40 +0200 Subject: [PATCH 03/10] chore: removing old plugin loader --- .../opentelemetry-instrumentation/README.md | 46 +-- .../src/autoLoader.ts | 12 +- .../src/autoLoaderUtils.ts | 19 +- .../src/platform/browser/index.ts | 1 - .../src/platform/browser/old/autoLoader.ts | 42 -- .../src/platform/node/index.ts | 1 - .../src/platform/node/old/PluginLoader.ts | 224 ----------- .../src/platform/node/old/autoLoader.ts | 81 ---- .../src/platform/node/old/utils.ts | 72 ---- .../src/types_internal.ts | 12 +- .../src/types_plugin_only.ts | 114 ------ .../test/browser/autoLoader.test.ts | 71 ---- .../test/browser/index-webpack.ts | 4 +- .../test/common/autoLoaderUtils.test.ts | 51 --- .../test/node/BasePlugin.ts | 44 --- .../test/node/PluginLoader.test.ts | 360 ------------------ .../test/node/autoLoader.test.ts | 101 ----- .../plugin-http-module/http-module.js | 22 -- .../plugin-http-module/index.js | 5 - .../plugin-http-module/package.json | 4 - .../plugin-notsupported-module/index.js | 5 - .../plugin-notsupported-module/package.json | 4 - .../simple-module.js | 25 -- .../plugin-simple-module/index.js | 5 - .../plugin-simple-module/package.json | 4 - .../plugin-simple-module/simple-module.js | 24 -- .../plugin-supported-module/index.js | 5 - .../plugin-supported-module/package.json | 4 - .../plugin-supported-module/simple-module.js | 25 -- .../already-require-module/index.js | 4 - .../already-require-module/package.json | 4 - .../node_modules/notsupported-module/index.js | 4 - .../notsupported-module/package.json | 4 - .../node/node_modules/random-module/index.js | 4 - .../node_modules/random-module/package.json | 4 - .../node/node_modules/simple-module/index.js | 4 - .../node_modules/simple-module/package.json | 4 - .../node_modules/supported-module/index.js | 4 - .../supported-module/package.json | 4 - .../test/node/utils.test.ts | 92 ----- packages/opentelemetry-sdk-node/README.md | 15 +- .../opentelemetry-sdk-node/test/sdk.test.ts | 3 - tsconfig.json | 12 - 43 files changed, 36 insertions(+), 1513 deletions(-) delete mode 100644 packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts delete mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts delete mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts delete mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts delete mode 100644 packages/opentelemetry-instrumentation/src/types_plugin_only.ts delete mode 100644 packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts delete mode 100644 packages/opentelemetry-instrumentation/test/node/BasePlugin.ts delete mode 100644 packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts delete mode 100644 packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js delete mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json delete mode 100644 packages/opentelemetry-instrumentation/test/node/utils.test.ts diff --git a/packages/opentelemetry-instrumentation/README.md b/packages/opentelemetry-instrumentation/README.md index 802440fa79..36c6f14f85 100644 --- a/packages/opentelemetry-instrumentation/README.md +++ b/packages/opentelemetry-instrumentation/README.md @@ -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/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/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-sdk-node/README.md b/packages/opentelemetry-sdk-node/README.md index 722086cec9..3c2a424861 100644 --- a/packages/opentelemetry-sdk-node/README.md +++ b/packages/opentelemetry-sdk-node/README.md @@ -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/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/tsconfig.json b/tsconfig.json index 9a41503863..ee42f486f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -68,18 +68,6 @@ { "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" }, From 7ba6b8cc0aacab2253bb949e75d290641e9cd6e5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 20:36:32 +0200 Subject: [PATCH 04/10] chore: removing meta packages --- metapackages/plugins-node-core/README.md | 37 --------------------- metapackages/plugins-node-core/package.json | 24 ------------- metapackages/plugins-web-core/README.md | 31 ----------------- metapackages/plugins-web-core/package.json | 21 ------------ 4 files changed, 113 deletions(-) delete mode 100644 metapackages/plugins-node-core/README.md delete mode 100644 metapackages/plugins-node-core/package.json delete mode 100644 metapackages/plugins-web-core/README.md delete mode 100644 metapackages/plugins-web-core/package.json 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 8779e8b6f5..0000000000 --- a/metapackages/plugins-node-core/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@opentelemetry/plugins-node-core", - "version": "0.18.2", - "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.2", - "@opentelemetry/plugin-grpc-js": "^0.18.2", - "@opentelemetry/plugin-http": "^0.18.2", - "@opentelemetry/plugin-https": "^0.18.2" - } -} 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 74c1ed0e3f..0000000000 --- a/metapackages/plugins-web-core/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@opentelemetry/plugins-web-core", - "version": "0.18.2", - "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.2" - } -} From d01175f54f661fed29b05e3c262597bf3e299c5f Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 20:52:40 +0200 Subject: [PATCH 05/10] chore: removing base plugin --- .../src/platform/browser/BasePlugin.ts | 38 - .../src/platform/browser/index.ts | 1 - .../src/platform/node/BasePlugin.ts | 134 --- .../src/platform/node/index.ts | 1 - .../opentelemetry-core/test/index-webpack.ts | 7 +- .../test/platform/BasePlugin.test.ts | 82 -- .../test/platform/browser/BasePlugin.test.ts | 46 - .../opentelemetry-core/test/utils/url.test.ts | 2 +- .../opentelemetry-grpc-utils/.eslintignore | 1 - .../opentelemetry-grpc-utils/.eslintrc.js | 7 - packages/opentelemetry-grpc-utils/.npmignore | 4 - packages/opentelemetry-grpc-utils/LICENSE | 201 ----- packages/opentelemetry-grpc-utils/README.md | 67 -- .../opentelemetry-grpc-utils/package.json | 75 -- .../opentelemetry-grpc-utils/src/version.ts | 18 - .../test/fixtures/grpc-test.proto | 19 - .../test/grpcUtils.test.ts | 830 ------------------ .../opentelemetry-grpc-utils/test/index.ts | 16 - .../test/utils/assertionUtils.ts | 80 -- .../opentelemetry-grpc-utils/tsconfig.json | 28 - tsconfig.json | 3 - 21 files changed, 2 insertions(+), 1658 deletions(-) delete mode 100644 packages/opentelemetry-core/src/platform/browser/BasePlugin.ts delete mode 100644 packages/opentelemetry-core/src/platform/node/BasePlugin.ts delete mode 100644 packages/opentelemetry-core/test/platform/BasePlugin.test.ts delete mode 100644 packages/opentelemetry-core/test/platform/browser/BasePlugin.test.ts delete mode 100644 packages/opentelemetry-grpc-utils/.eslintignore delete mode 100644 packages/opentelemetry-grpc-utils/.eslintrc.js delete mode 100644 packages/opentelemetry-grpc-utils/.npmignore delete mode 100644 packages/opentelemetry-grpc-utils/LICENSE delete mode 100644 packages/opentelemetry-grpc-utils/README.md delete mode 100644 packages/opentelemetry-grpc-utils/package.json delete mode 100644 packages/opentelemetry-grpc-utils/src/version.ts delete mode 100644 packages/opentelemetry-grpc-utils/test/fixtures/grpc-test.proto delete mode 100644 packages/opentelemetry-grpc-utils/test/grpcUtils.test.ts delete mode 100644 packages/opentelemetry-grpc-utils/test/index.ts delete mode 100644 packages/opentelemetry-grpc-utils/test/utils/assertionUtils.ts delete mode 100644 packages/opentelemetry-grpc-utils/tsconfig.json 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/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-grpc-utils/.eslintignore b/packages/opentelemetry-grpc-utils/.eslintignore deleted file mode 100644 index 378eac25d3..0000000000 --- a/packages/opentelemetry-grpc-utils/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/packages/opentelemetry-grpc-utils/.eslintrc.js b/packages/opentelemetry-grpc-utils/.eslintrc.js deleted file mode 100644 index f726f3becb..0000000000 --- a/packages/opentelemetry-grpc-utils/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - "env": { - "mocha": true, - "node": true - }, - ...require('../../eslint.config.js') -} 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/LICENSE b/packages/opentelemetry-grpc-utils/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/packages/opentelemetry-grpc-utils/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-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 1993b206bc..0000000000 --- a/packages/opentelemetry-grpc-utils/package.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "name": "@opentelemetry/grpc-utils", - "version": "0.18.2", - "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/api": "^1.0.0-rc.0", - "@opentelemetry/context-async-hooks": "^0.18.2", - "@opentelemetry/node": "^0.18.2", - "@opentelemetry/tracing": "^0.18.2", - "@types/mocha": "8.2.2", - "@types/node": "14.14.37", - "@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" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0-rc.0" - }, - "dependencies": { - "@opentelemetry/core": "^0.18.2", - "@opentelemetry/semantic-conventions": "^0.18.2", - "shimmer": "1.2.1" - } -} diff --git a/packages/opentelemetry-grpc-utils/src/version.ts b/packages/opentelemetry-grpc-utils/src/version.ts deleted file mode 100644 index 6fff6ffcee..0000000000 --- a/packages/opentelemetry-grpc-utils/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.2'; 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/tsconfig.json b/tsconfig.json index ee42f486f9..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" }, From 6397a3c79fa3e48962aefc805ef1b79f387e04d6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 6 Apr 2021 21:16:40 +0200 Subject: [PATCH 06/10] chore: removing plugins - updating readme --- README.md | 18 ++++++++---- .../README.md | 19 ++++++------ .../README.md | 19 +++++------- .../README.md | 21 +++++--------- .../README.md | 25 ++++++++++------ .../opentelemetry-instrumentation/README.md | 4 +-- packages/opentelemetry-node/README.md | 29 ++++--------------- 7 files changed, 62 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 1da193aef2..4629f301cb 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,11 +196,11 @@ 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 @@ -221,7 +221,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] @@ -244,6 +244,14 @@ To request automatic tracing support for a module not on this list, please [file | [@opentelemetry/shim-opentracing][otel-shim-opentracing] | OpenTracing shim allows existing OpenTracing instrumentation to report to OpenTelemetry | ## Upgrade guidelines +\ +### 0.19.0 to 1.0.0-rc.0 + +- All plugins have been removed use instrumentations. +```javascript + +``` + ### 0.18.0 to 0.19.0 diff --git a/packages/opentelemetry-instrumentation-fetch/README.md b/packages/opentelemetry-instrumentation-fetch/README.md index 13bfec30e4..8adf1a30c4 100644 --- a/packages/opentelemetry-instrumentation-fetch/README.md +++ b/packages/opentelemetry-instrumentation-fetch/README.md @@ -17,16 +17,10 @@ npm install --save @opentelemetry/instrumentation-fetch ```js 'use strict'; -import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing'; -import { WebTracerProvider } from '@opentelemetry/web'; -import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'; -import { ZoneContextManager } from '@opentelemetry/context-zone'; - -const provider = new WebTracerProvider({ - plugins: [ - new FetchInstrumentation(), - ], -}); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const { WebTracerProvider } = require('@opentelemetry/web'); + +const provider = new WebTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); @@ -34,6 +28,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(); 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-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-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/README.md b/packages/opentelemetry-instrumentation/README.md index 36c6f14f85..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'); diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md index d1cc54c099..df9fb3e694 100644 --- a/packages/opentelemetry-node/README.md +++ b/packages/opentelemetry-node/README.md @@ -87,6 +87,8 @@ In the following example: ```javascript const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); +const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); const provider = new NodeTracerProvider(); @@ -96,36 +98,17 @@ registerInstrumentations({ tracerProvider: provider, instrumentations: [ new GraphQLInstrumentation(), - // for older plugins you can just copy paste the old configuration - { - plugins: { - express: { - enabled: false, + new HttpInstrumentation({ + requestHook: (span, request) => { + span.setAttribute("custom request hook attribute", "request"); }, - http: { - requestHook: (span, request) => { - span.setAttribute("custom request hook attribute", "request"); - }, - }, - customPlugin: { - path: "/path/to/custom/module", - }, - }, - } + }), ], }); ``` -### 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. From 93013a394335992f7c673be26ddfd4ca55ca5836 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Wed, 7 Apr 2021 18:27:10 +0200 Subject: [PATCH 07/10] chore: removing remaining plugins configurations, updating readme, removing base abstract class --- doc/instrumentation-guide.md | 55 ++-------- getting-started/README.md | 45 +++++--- packages/opentelemetry-core/src/index.ts | 1 - .../src/platform/BaseAbstractPlugin.ts | 50 --------- .../opentelemetry-core/src/trace/Plugin.ts | 100 ------------------ packages/opentelemetry-node/README.md | 17 +-- .../src/NodeTracerProvider.ts | 7 -- packages/opentelemetry-node/src/config.ts | 5 +- .../test/NodeTracerProvider.test.ts | 16 --- packages/opentelemetry-sdk-node/README.md | 2 +- packages/opentelemetry-sdk-node/src/sdk.ts | 5 +- packages/opentelemetry-sdk-node/src/types.ts | 2 - .../src/WebTracerProvider.ts | 14 +-- .../test/WebTracerProvider.test.ts | 17 --- 14 files changed, 44 insertions(+), 292 deletions(-) delete mode 100644 packages/opentelemetry-core/src/platform/BaseAbstractPlugin.ts delete mode 100644 packages/opentelemetry-core/src/trace/Plugin.ts 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/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/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/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-node/README.md b/packages/opentelemetry-node/README.md index df9fb3e694..c77af48a36 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,14 @@ 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 express plugin is enabled - 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. ```javascript -const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); @@ -97,7 +90,7 @@ const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, instrumentations: [ - new GraphQLInstrumentation(), + new ExpressInstrumentation(), new HttpInstrumentation({ requestHook: (span, request) => { span.setAttribute("custom request hook attribute", "request"); diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index 7afc986672..19fb8976f3 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; import { AsyncHooksContextManager, AsyncLocalStorageContextManager, @@ -36,12 +35,6 @@ import { NodeTracerConfig } from './config'; export class NodeTracerProvider extends BasicTracerProvider { constructor(config: NodeTracerConfig = {}) { super(config); - if (config.plugins) { - diag.warn( - 'plugins options was removed, please use' + - ' "registerInstrumentations" to load plugins' - ); - } } register(config: SDKRegistrationConfig = {}) { 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/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts index 9b45531134..110ef6973a 100644 --- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts +++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts @@ -20,14 +20,12 @@ import { setSpan, setSpanContext, getSpan, - diag, } 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 +77,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()', () => { diff --git a/packages/opentelemetry-sdk-node/README.md b/packages/opentelemetry-sdk-node/README.md index 3c2a424861..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 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-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/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({}); From fe12bbc7908b14be760973c093c7a38a290e3fb5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Wed, 7 Apr 2021 18:27:10 +0200 Subject: [PATCH 08/10] chore: removing remaining plugins configurations, updating readme, removing base abstract class --- README.md | 4 +- doc/instrumentation-guide.md | 55 ++-------- getting-started/README.md | 45 +++++--- packages/opentelemetry-core/src/index.ts | 1 - .../src/platform/BaseAbstractPlugin.ts | 50 --------- .../opentelemetry-core/src/trace/Plugin.ts | 100 ------------------ packages/opentelemetry-node/README.md | 17 +-- .../src/NodeTracerProvider.ts | 7 -- packages/opentelemetry-node/src/config.ts | 5 +- .../test/NodeTracerProvider.test.ts | 16 --- packages/opentelemetry-sdk-node/README.md | 2 +- packages/opentelemetry-sdk-node/src/sdk.ts | 5 +- packages/opentelemetry-sdk-node/src/types.ts | 2 - .../src/WebTracerProvider.ts | 14 +-- .../test/WebTracerProvider.test.ts | 17 --- 15 files changed, 46 insertions(+), 294 deletions(-) delete mode 100644 packages/opentelemetry-core/src/platform/BaseAbstractPlugin.ts delete mode 100644 packages/opentelemetry-core/src/trace/Plugin.ts diff --git a/README.md b/README.md index 4629f301cb..211f3d3a37 100644 --- a/README.md +++ b/README.md @@ -244,10 +244,10 @@ To request automatic tracing support for a module not on this list, please [file | [@opentelemetry/shim-opentracing][otel-shim-opentracing] | OpenTracing shim allows existing OpenTracing instrumentation to report to OpenTelemetry | ## Upgrade guidelines -\ + ### 0.19.0 to 1.0.0-rc.0 -- All plugins have been removed use instrumentations. +- All plugins have been removed in favor of instrumentations. ```javascript ``` 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/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/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/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-node/README.md b/packages/opentelemetry-node/README.md index df9fb3e694..c77af48a36 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,14 @@ 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 express plugin is enabled - 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. ```javascript -const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); @@ -97,7 +90,7 @@ const provider = new NodeTracerProvider(); registerInstrumentations({ tracerProvider: provider, instrumentations: [ - new GraphQLInstrumentation(), + new ExpressInstrumentation(), new HttpInstrumentation({ requestHook: (span, request) => { span.setAttribute("custom request hook attribute", "request"); diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts index 7afc986672..19fb8976f3 100644 --- a/packages/opentelemetry-node/src/NodeTracerProvider.ts +++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { diag } from '@opentelemetry/api'; import { AsyncHooksContextManager, AsyncLocalStorageContextManager, @@ -36,12 +35,6 @@ import { NodeTracerConfig } from './config'; export class NodeTracerProvider extends BasicTracerProvider { constructor(config: NodeTracerConfig = {}) { super(config); - if (config.plugins) { - diag.warn( - 'plugins options was removed, please use' + - ' "registerInstrumentations" to load plugins' - ); - } } register(config: SDKRegistrationConfig = {}) { 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/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts index 9b45531134..110ef6973a 100644 --- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts +++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts @@ -20,14 +20,12 @@ import { setSpan, setSpanContext, getSpan, - diag, } 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 +77,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()', () => { diff --git a/packages/opentelemetry-sdk-node/README.md b/packages/opentelemetry-sdk-node/README.md index 3c2a424861..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 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-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/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({}); From 413a9ce42390d6bdf78186357cbdce391a7c7cb0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Wed, 7 Apr 2021 18:37:29 +0200 Subject: [PATCH 09/10] chore: linting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 211f3d3a37..2c18538a81 100644 --- a/README.md +++ b/README.md @@ -248,11 +248,11 @@ To request automatic tracing support for a module not on this list, please [file ### 0.19.0 to 1.0.0-rc.0 - All plugins have been removed in favor of instrumentations. + ```javascript ``` - ### 0.18.0 to 0.19.0 - 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. From a178b88a8f1f157412d1b6ce5bd45c8d9569282f Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Thu, 8 Apr 2021 18:45:46 +0200 Subject: [PATCH 10/10] chore: addressing review comments --- README.md | 4 ---- packages/opentelemetry-instrumentation-fetch/README.md | 7 +++++-- packages/opentelemetry-node/README.md | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2c18538a81..30d8ba27cf 100644 --- a/README.md +++ b/README.md @@ -249,10 +249,6 @@ To request automatic tracing support for a module not on this list, please [file - All plugins have been removed in favor of instrumentations. -```javascript - -``` - ### 0.18.0 to 0.19.0 - 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. diff --git a/packages/opentelemetry-instrumentation-fetch/README.md b/packages/opentelemetry-instrumentation-fetch/README.md index 8adf1a30c4..6c04e78606 100644 --- a/packages/opentelemetry-instrumentation-fetch/README.md +++ b/packages/opentelemetry-instrumentation-fetch/README.md @@ -17,8 +17,11 @@ npm install --save @opentelemetry/instrumentation-fetch ```js 'use strict'; -const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const { WebTracerProvider } = require('@opentelemetry/web'); +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(); diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md index c77af48a36..04a4b64abb 100644 --- a/packages/opentelemetry-node/README.md +++ b/packages/opentelemetry-node/README.md @@ -76,8 +76,8 @@ const http = require('http'); In the following example: -- the express plugin is enabled -- the http plugin has a custom config for a `requestHook` +- the express instrumentation is enabled +- the http instrumentation has a custom config for a `requestHook` ```javascript const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');