From 9336cdcbd3e593d07b1b46876732c9f2e82b7e03 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 2 Feb 2021 02:12:55 +0100 Subject: [PATCH] User interaction (#324) * chore: moving user interaction plugin into instrumentation * chore: adding example for user interaction * chore: renaming plugin to instrumentation * chore: renaming plugin to instrumentation --- .circleci/config.yml | 2 +- README.md | 4 +- examples/web/README.md | 28 ++++++ .../web/examples/user-interaction/index.html | 39 ++++++++ .../web/examples/user-interaction/index.js | 87 ++++++++++++++++++ examples/web/package.json | 47 ++++++++++ examples/web/webpack.config.js | 55 +++++++++++ .../.eslintignore | 0 .../.eslintrc.js | 0 .../LICENSE | 0 .../README.md | 20 ++-- .../images/click-sync.jpg | Bin .../images/click.jpg | Bin .../images/main-sync.jpg | Bin .../images/main.jpg | Bin .../karma.conf.js | 0 .../package.json | 6 +- .../src/enums/AttributeNames.ts | 0 .../src/index.ts | 0 .../src/types.ts | 0 .../src/userInteraction.ts | 87 ++++++++++-------- .../src/version.ts | 0 .../test/helper.test.ts | 0 .../test/index-webpack.ts | 0 .../test/userInteraction.nozone.test.ts | 41 +++++---- .../test/userInteraction.test.ts | 34 ++++--- .../tsconfig.json | 0 27 files changed, 370 insertions(+), 80 deletions(-) create mode 100644 examples/web/README.md create mode 100644 examples/web/examples/user-interaction/index.html create mode 100644 examples/web/examples/user-interaction/index.js create mode 100644 examples/web/package.json create mode 100644 examples/web/webpack.config.js rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/.eslintignore (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/.eslintrc.js (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/LICENSE (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/README.md (82%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/images/click-sync.jpg (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/images/click.jpg (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/images/main-sync.jpg (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/images/main.jpg (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/karma.conf.js (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/package.json (94%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/src/enums/AttributeNames.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/src/index.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/src/types.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/src/userInteraction.ts (88%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/src/version.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/test/helper.test.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/test/index-webpack.ts (100%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/test/userInteraction.nozone.test.ts (94%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/test/userInteraction.test.ts (93%) rename plugins/web/{opentelemetry-plugin-user-interaction => opentelemetry-instrumentation-user-interaction}/tsconfig.json (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ccf82e727..1280d7b4b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,7 +61,7 @@ cache_1: &cache_1 - plugins/node/opentelemetry-plugin-redis/node_modules - plugins/web/opentelemetry-plugin-document-load/node_modules - plugins/web/opentelemetry-plugin-react-load/node_modules - - plugins/web/opentelemetry-plugin-user-interaction/node_modules + - plugins/web/opentelemetry-instrumentation-user-interaction/node_modules - propagators/opentelemetry-propagator-jaeger/node_modules - propagators/opentelemetry-propagator-grpc-census-binary/node_modules diff --git a/README.md b/README.md index 4d6244703c..29b6585df0 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ OpenTelemetry can collect tracing data automatically using plugins. Vendors/User - [@opentelemetry/plugin-document-load][otel-plugin-document-load] - [@opentelemetry/plugin-xml-http-request][otel-plugin-xml-http-request] -- [@opentelemetry/plugin-user-interaction][otel-plugin-user-interaction] +- [@opentelemetry/instrumentation-user-interaction][otel-instrumentation-user-interaction] - [@opentelemetry/plugin-react-load][otel-plugin-react-load] ### Metapackages @@ -130,7 +130,7 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [otel-plugin-pg-pool]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-plugin-pg-pool [otel-plugin-pg]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-plugin-pg [otel-plugin-redis]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-plugin-redis -[otel-plugin-user-interaction]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-plugin-user-interaction +[otel-instrumentation-user-interaction]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/web/opentelemetry-instrumentation-user-interaction [otel-plugin-xml-http-request]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-xml-http-request [otel-plugin-express]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-plugin-express [otel-plugins-node-core-and-contrib]: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/metapackages/plugins-node-core-and-contrib diff --git a/examples/web/README.md b/examples/web/README.md new file mode 100644 index 0000000000..1d92cd8220 --- /dev/null +++ b/examples/web/README.md @@ -0,0 +1,28 @@ +# Overview + +This example shows how to use [@opentelemetry/web](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-web) with different instrumentations from contrib repo in a browser. + +## Installation + +```sh +# from this directory +npm install +``` + +## Run the Application + +```sh +# from this directory +npm start +``` + +By default, the application will run on port `8090`. + +## Useful links + +- For more information on OpenTelemetry, visit: +- For more information on web tracing, visit: + +## LICENSE + +Apache License 2.0 diff --git a/examples/web/examples/user-interaction/index.html b/examples/web/examples/user-interaction/index.html new file mode 100644 index 0000000000..958fe75efc --- /dev/null +++ b/examples/web/examples/user-interaction/index.html @@ -0,0 +1,39 @@ + + + + + + User Interaction Example + + + + + + + + + + Example of using Web Tracer with UserInteractionInstrumentation and XMLHttpRequestInstrumentation with console exporter and collector exporter + +
+ +
+
+
+
+
+
+
+
+ + + + diff --git a/examples/web/examples/user-interaction/index.js b/examples/web/examples/user-interaction/index.js new file mode 100644 index 0000000000..3649a63421 --- /dev/null +++ b/examples/web/examples/user-interaction/index.js @@ -0,0 +1,87 @@ +import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing'; +import { WebTracerProvider } from '@opentelemetry/web'; +import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { CollectorTraceExporter } from '@opentelemetry/exporter-collector'; +import { B3Propagator } from '@opentelemetry/propagator-b3'; +import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; + +const providerWithZone = new WebTracerProvider(); + +providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); +providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new CollectorTraceExporter())); + +providerWithZone.register({ + contextManager: new ZoneContextManager(), + propagator: new B3Propagator(), +}); + +registerInstrumentations({ + instrumentations: [ + new UserInteractionInstrumentation(), + new XMLHttpRequestInstrumentation({ + ignoreUrls: [/localhost/], + propagateTraceHeaderCorsUrls: [ + 'http://localhost:8090', + ], + }), + ], + tracerProvider: providerWithZone, +}); + +let lastButtonId = 0; + +function btnAddClick() { + lastButtonId++; + const btn = document.createElement('button'); + // for easier testing of element xpath + let navigate = false; + if (lastButtonId % 2 === 0) { + btn.setAttribute('id', `button${lastButtonId}`); + navigate = true; + } + btn.setAttribute('class', `buttonClass${lastButtonId}`); + btn.append(document.createTextNode(`Click ${lastButtonId}`)); + btn.addEventListener('click', onClick.bind(this, navigate)); + document.querySelector('#buttons').append(btn); +} + +function prepareClickEvents() { + for (let i = 0; i < 5; i++) { + btnAddClick(); + } + const btnAdd = document.getElementById('btnAdd'); + btnAdd.addEventListener('click', btnAddClick); +} + +function onClick(navigate) { + if (navigate) { + history.pushState({ test: 'testing' }, '', `${location.pathname}`); + history.pushState({ test: 'testing' }, '', `${location.pathname}#foo=bar1`); + } + getData('https://httpbin.org/get?a=1').then(() => { + getData('https://httpbin.org/get?a=1').then(() => { + console.log('data downloaded 2'); + }); + getData('https://httpbin.org/get?a=1').then(() => { + console.log('data downloaded 3'); + }); + console.log('data downloaded 1'); + }); +} + +function getData(url, resolve) { + return new Promise(async (resolve, reject) => { + const req = new XMLHttpRequest(); + req.open('GET', url, true); + req.setRequestHeader('Content-Type', 'application/json'); + req.setRequestHeader('Accept', 'application/json'); + req.send(); + req.onload = function () { + resolve(); + }; + }); +} + +window.addEventListener('load', prepareClickEvents); diff --git a/examples/web/package.json b/examples/web/package.json new file mode 100644 index 0000000000..ce4b69e33d --- /dev/null +++ b/examples/web/package.json @@ -0,0 +1,47 @@ +{ + "name": "web-examples", + "private": true, + "version": "0.12.0", + "description": "Example of using web plugins in browser", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server -d --progress --colors --port 8090 --config webpack.config.js --hot --inline --host 0.0.0.0 --content-base examples" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js-contrib.git" + }, + "keywords": [ + "opentelemetry", + "tracing", + "web" + ], + "engines": { + "node": ">=8" + }, + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/open-telemetry/opentelemetry-js-contrib/issues" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "babel-loader": "^8.2.2", + "ts-loader": "^6.2.2", + "webpack": "^4.44.2", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0", + "webpack-merge": "^4.2.2" + }, + "dependencies": { + "@opentelemetry/context-zone": "^0.15.0", + "@opentelemetry/core": "^0.15.0", + "@opentelemetry/exporter-collector": "^0.15.0", + "@opentelemetry/instrumentation-xml-http-request": "^0.15.0", + "@opentelemetry/instrumentation-user-interaction": "^0.12.1", + "@opentelemetry/propagator-b3": "^0.15.0", + "@opentelemetry/tracing": "^0.15.0", + "@opentelemetry/web": "^0.15.0" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib#readme" +} diff --git a/examples/web/webpack.config.js b/examples/web/webpack.config.js new file mode 100644 index 0000000000..9637afc950 --- /dev/null +++ b/examples/web/webpack.config.js @@ -0,0 +1,55 @@ +const webpack = require('webpack'); +const webpackMerge = require('webpack-merge'); +const path = require('path'); + +const directory = path.resolve(__dirname); + +const common = { + mode: 'development', + entry: { + 'user-interaction': 'examples/user-interaction/index.js', + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + // sourceMapFilename: '[file].map', + }, + target: 'web', + module: { + rules: [ + { + test: /\.js[x]?$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.ts$/, + exclude: /(node_modules)/, + use: { + loader: 'ts-loader', + }, + }, + ], + }, + resolve: { + modules: [ + path.resolve(directory), + 'node_modules', + ], + extensions: ['.ts', '.js', '.jsx', '.json'], + }, +}; + +module.exports = webpackMerge(common, { + devtool: 'eval-source-map', + devServer: { + contentBase: path.resolve(__dirname), + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('development'), + }), + ], +}); diff --git a/plugins/web/opentelemetry-plugin-user-interaction/.eslintignore b/plugins/web/opentelemetry-instrumentation-user-interaction/.eslintignore similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/.eslintignore rename to plugins/web/opentelemetry-instrumentation-user-interaction/.eslintignore diff --git a/plugins/web/opentelemetry-plugin-user-interaction/.eslintrc.js b/plugins/web/opentelemetry-instrumentation-user-interaction/.eslintrc.js similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/.eslintrc.js rename to plugins/web/opentelemetry-instrumentation-user-interaction/.eslintrc.js diff --git a/plugins/web/opentelemetry-plugin-user-interaction/LICENSE b/plugins/web/opentelemetry-instrumentation-user-interaction/LICENSE similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/LICENSE rename to plugins/web/opentelemetry-instrumentation-user-interaction/LICENSE diff --git a/plugins/web/opentelemetry-plugin-user-interaction/README.md b/plugins/web/opentelemetry-instrumentation-user-interaction/README.md similarity index 82% rename from plugins/web/opentelemetry-plugin-user-interaction/README.md rename to plugins/web/opentelemetry-instrumentation-user-interaction/README.md index 662ce43fd4..cd31cf80ed 100644 --- a/plugins/web/opentelemetry-plugin-user-interaction/README.md +++ b/plugins/web/opentelemetry-instrumentation-user-interaction/README.md @@ -1,4 +1,4 @@ -# OpenTelemetry UserInteraction Plugin for web +# OpenTelemetry UserInteraction Instrumentation for web [![Gitter chat][gitter-image]][gitter-url] [![NPM Published Version][npm-img]][npm-url] [![dependencies][dependencies-image]][dependencies-url] @@ -14,7 +14,7 @@ Without [zone-js] it will still work but with limited support. ## Installation ```bash -npm install --save @opentelemetry/plugin-user-interaction +npm install --save @opentelemetry/instrumentation-user-interaction ``` ## Usage @@ -22,7 +22,7 @@ npm install --save @opentelemetry/plugin-user-interaction ```js import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; -import { UserInteractionPlugin } from '@opentelemetry/plugin-user-interaction'; +import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction'; import { ZoneContextManager } from '@opentelemetry/context-zone'; // or if you already have zone.js // import { ZoneContextManager } from '@opentelemetry/context-zone-peer-dep'; @@ -30,7 +30,7 @@ import { ZoneContextManager } from '@opentelemetry/context-zone'; const provider = new WebTracerProvider({ contextManager: new ZoneContextManager(), // optional plugins: [ - new UserInteractionPlugin() + new UserInteractionInstrumentation() ] }); provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); @@ -94,12 +94,12 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [gitter-url]: https://gitter.im/open-telemetry/opentelemetry-node?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [license-url]: https://github.com/open-telemetry/opentelemetry-js/blob/main/LICENSE [license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat -[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-plugin-user-interaction -[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-user-interaction -[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-plugin-user-interaction -[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-plugin-user-interaction&type=dev -[npm-url]: https://www.npmjs.com/package/@opentelemetry/plugin-user-interaction -[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Fplugin-user-interaction.svg +[dependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/status.svg?path=packages/opentelemetry-instrumentation-user-interaction +[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-instrumentation-user-interaction +[devDependencies-image]: https://david-dm.org/open-telemetry/opentelemetry-js/dev-status.svg?path=packages/opentelemetry-instrumentation-user-interaction +[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js?path=packages%2Fopentelemetry-instrumentation-user-interaction&type=dev +[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-user-interaction +[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-user-interaction.svg [zone-js]: https://www.npmjs.com/package/zone.js [@opentelemetry/context-zone]: https://www.npmjs.com/package/@opentelemetry/context-zone diff --git a/plugins/web/opentelemetry-plugin-user-interaction/images/click-sync.jpg b/plugins/web/opentelemetry-instrumentation-user-interaction/images/click-sync.jpg similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/images/click-sync.jpg rename to plugins/web/opentelemetry-instrumentation-user-interaction/images/click-sync.jpg diff --git a/plugins/web/opentelemetry-plugin-user-interaction/images/click.jpg b/plugins/web/opentelemetry-instrumentation-user-interaction/images/click.jpg similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/images/click.jpg rename to plugins/web/opentelemetry-instrumentation-user-interaction/images/click.jpg diff --git a/plugins/web/opentelemetry-plugin-user-interaction/images/main-sync.jpg b/plugins/web/opentelemetry-instrumentation-user-interaction/images/main-sync.jpg similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/images/main-sync.jpg rename to plugins/web/opentelemetry-instrumentation-user-interaction/images/main-sync.jpg diff --git a/plugins/web/opentelemetry-plugin-user-interaction/images/main.jpg b/plugins/web/opentelemetry-instrumentation-user-interaction/images/main.jpg similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/images/main.jpg rename to plugins/web/opentelemetry-instrumentation-user-interaction/images/main.jpg diff --git a/plugins/web/opentelemetry-plugin-user-interaction/karma.conf.js b/plugins/web/opentelemetry-instrumentation-user-interaction/karma.conf.js similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/karma.conf.js rename to plugins/web/opentelemetry-instrumentation-user-interaction/karma.conf.js diff --git a/plugins/web/opentelemetry-plugin-user-interaction/package.json b/plugins/web/opentelemetry-instrumentation-user-interaction/package.json similarity index 94% rename from plugins/web/opentelemetry-plugin-user-interaction/package.json rename to plugins/web/opentelemetry-instrumentation-user-interaction/package.json index eb634de4a7..f1d53b997a 100644 --- a/plugins/web/opentelemetry-plugin-user-interaction/package.json +++ b/plugins/web/opentelemetry-instrumentation-user-interaction/package.json @@ -1,5 +1,5 @@ { - "name": "@opentelemetry/plugin-user-interaction", + "name": "@opentelemetry/instrumentation-user-interaction", "version": "0.13.0", "description": "OpenTelemetry UserInteraction automatic instrumentation package.", "main": "build/src/index.js", @@ -82,8 +82,8 @@ "dependencies": { "@opentelemetry/api": "^0.15.0", "@opentelemetry/core": "^0.15.0", - "@opentelemetry/web": "^0.15.0", - "shimmer": "^1.2.1" + "@opentelemetry/instrumentation": "^0.15.0", + "@opentelemetry/web": "^0.15.0" }, "peerDependencies": { "zone.js": "0.10.3" diff --git a/plugins/web/opentelemetry-plugin-user-interaction/src/enums/AttributeNames.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/src/enums/AttributeNames.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/src/enums/AttributeNames.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/src/enums/AttributeNames.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/src/index.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/src/index.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/src/index.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/src/index.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/src/types.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/src/types.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/src/types.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/src/types.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/src/userInteraction.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/src/userInteraction.ts similarity index 88% rename from plugins/web/opentelemetry-plugin-user-interaction/src/userInteraction.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/src/userInteraction.ts index 24517f23ef..e518f76901 100644 --- a/plugins/web/opentelemetry-plugin-user-interaction/src/userInteraction.ts +++ b/plugins/web/opentelemetry-instrumentation-user-interaction/src/userInteraction.ts @@ -14,11 +14,16 @@ * limitations under the License. */ +import { + isWrapped, + InstrumentationBase, + InstrumentationConfig, +} from '@opentelemetry/instrumentation'; + import * as api from '@opentelemetry/api'; import { getSpan, Span } from '@opentelemetry/api'; -import { BasePlugin, hrTime, isWrapped } from '@opentelemetry/core'; +import { hrTime } from '@opentelemetry/core'; import { getElementXPath } from '@opentelemetry/web'; -import * as shimmer from 'shimmer'; import { AttributeNames } from './enums/AttributeNames'; import { AsyncTask, @@ -37,7 +42,7 @@ const EVENT_NAVIGATION_NAME = 'Navigation:'; * If zone.js is available then it patches the zone otherwise it patches * addEventListener of HTMLElement */ -export class UserInteractionPlugin extends BasePlugin { +export class UserInteractionInstrumentation extends InstrumentationBase { readonly component: string = 'user-interaction'; readonly version = VERSION; moduleName = this.component; @@ -51,10 +56,12 @@ export class UserInteractionPlugin extends BasePlugin { // for event bubbling private _eventsSpanMap: WeakMap = new WeakMap(); - constructor() { - super('@opentelemetry/plugin-user-interaction', VERSION); + constructor(config?: InstrumentationConfig) { + super('@opentelemetry/instrumentation-user-interaction', VERSION, config); } + init() {} + /** * This will check if last task was timeout and will save the time to * fix the user interaction when nothing happens @@ -104,7 +111,7 @@ export class UserInteractionPlugin extends BasePlugin { } const xpath = getElementXPath(element, true); try { - const span = this._tracer.startSpan( + const span = this.tracer.startSpan( eventName, { attributes: { @@ -322,11 +329,11 @@ export class UserInteractionPlugin extends BasePlugin { _patchHistoryApi() { this._unpatchHistoryApi(); - shimmer.wrap(history, 'replaceState', this._patchHistoryMethod()); - shimmer.wrap(history, 'pushState', this._patchHistoryMethod()); - shimmer.wrap(history, 'back', this._patchHistoryMethod()); - shimmer.wrap(history, 'forward', this._patchHistoryMethod()); - shimmer.wrap(history, 'go', this._patchHistoryMethod()); + this._wrap(history, 'replaceState', this._patchHistoryMethod()); + this._wrap(history, 'pushState', this._patchHistoryMethod()); + this._wrap(history, 'back', this._patchHistoryMethod()); + this._wrap(history, 'forward', this._patchHistoryMethod()); + this._wrap(history, 'go', this._patchHistoryMethod()); } /** @@ -351,12 +358,11 @@ export class UserInteractionPlugin extends BasePlugin { * unpatch the history api methods */ _unpatchHistoryApi() { - if (isWrapped(history.replaceState)) - shimmer.unwrap(history, 'replaceState'); - if (isWrapped(history.pushState)) shimmer.unwrap(history, 'pushState'); - if (isWrapped(history.back)) shimmer.unwrap(history, 'back'); - if (isWrapped(history.forward)) shimmer.unwrap(history, 'forward'); - if (isWrapped(history.go)) shimmer.unwrap(history, 'go'); + if (isWrapped(history.replaceState)) this._unwrap(history, 'replaceState'); + if (isWrapped(history.pushState)) this._unwrap(history, 'pushState'); + if (isWrapped(history.back)) this._unwrap(history, 'back'); + if (isWrapped(history.forward)) this._unwrap(history, 'forward'); + if (isWrapped(history.go)) this._unwrap(history, 'go'); } /** @@ -511,9 +517,9 @@ export class UserInteractionPlugin extends BasePlugin { } /** - * implements patch function + * implements enable function */ - protected patch() { + enable() { const ZoneWithPrototype = this.getZoneWithPrototype(); this._logger.debug( 'applying patch to', @@ -524,30 +530,30 @@ export class UserInteractionPlugin extends BasePlugin { ); if (ZoneWithPrototype) { if (isWrapped(ZoneWithPrototype.prototype.runTask)) { - shimmer.unwrap(ZoneWithPrototype.prototype, 'runTask'); + this._unwrap(ZoneWithPrototype.prototype, 'runTask'); this._logger.debug('removing previous patch from method runTask'); } if (isWrapped(ZoneWithPrototype.prototype.scheduleTask)) { - shimmer.unwrap(ZoneWithPrototype.prototype, 'scheduleTask'); + this._unwrap(ZoneWithPrototype.prototype, 'scheduleTask'); this._logger.debug('removing previous patch from method scheduleTask'); } if (isWrapped(ZoneWithPrototype.prototype.cancelTask)) { - shimmer.unwrap(ZoneWithPrototype.prototype, 'cancelTask'); + this._unwrap(ZoneWithPrototype.prototype, 'cancelTask'); this._logger.debug('removing previous patch from method cancelTask'); } this._zonePatched = true; - shimmer.wrap( + this._wrap( ZoneWithPrototype.prototype, 'runTask', this._patchZoneRunTask() ); - shimmer.wrap( + this._wrap( ZoneWithPrototype.prototype, 'scheduleTask', this._patchZoneScheduleTask() ); - shimmer.wrap( + this._wrap( ZoneWithPrototype.prototype, 'cancelTask', this._patchZoneCancelTask() @@ -555,23 +561,23 @@ export class UserInteractionPlugin extends BasePlugin { } else { this._zonePatched = false; if (isWrapped(HTMLElement.prototype.addEventListener)) { - shimmer.unwrap(HTMLElement.prototype, 'addEventListener'); + this._unwrap(HTMLElement.prototype, 'addEventListener'); this._logger.debug( 'removing previous patch from method addEventListener' ); } if (isWrapped(HTMLElement.prototype.removeEventListener)) { - shimmer.unwrap(HTMLElement.prototype, 'removeEventListener'); + this._unwrap(HTMLElement.prototype, 'removeEventListener'); this._logger.debug( 'removing previous patch from method removeEventListener' ); } - shimmer.wrap( + this._wrap( HTMLElement.prototype, 'addEventListener', this._patchElement() ); - shimmer.wrap( + this._wrap( HTMLElement.prototype, 'removeEventListener', this._patchRemoveEventListener() @@ -579,13 +585,12 @@ export class UserInteractionPlugin extends BasePlugin { } this._patchHistoryApi(); - return this._moduleExports; } /** * implements unpatch function */ - protected unpatch() { + disable() { const ZoneWithPrototype = this.getZoneWithPrototype(); this._logger.debug( 'removing patch from', @@ -595,12 +600,22 @@ export class UserInteractionPlugin extends BasePlugin { !!ZoneWithPrototype ); if (ZoneWithPrototype && this._zonePatched) { - shimmer.unwrap(ZoneWithPrototype.prototype, 'runTask'); - shimmer.unwrap(ZoneWithPrototype.prototype, 'scheduleTask'); - shimmer.unwrap(ZoneWithPrototype.prototype, 'cancelTask'); + if (isWrapped(ZoneWithPrototype.prototype.runTask)) { + this._unwrap(ZoneWithPrototype.prototype, 'runTask'); + } + if (isWrapped(ZoneWithPrototype.prototype.scheduleTask)) { + this._unwrap(ZoneWithPrototype.prototype, 'scheduleTask'); + } + if (isWrapped(ZoneWithPrototype.prototype.cancelTask)) { + this._unwrap(ZoneWithPrototype.prototype, 'cancelTask'); + } } else { - shimmer.unwrap(HTMLElement.prototype, 'addEventListener'); - shimmer.unwrap(HTMLElement.prototype, 'removeEventListener'); + if (isWrapped(HTMLElement.prototype.addEventListener)) { + this._unwrap(HTMLElement.prototype, 'addEventListener'); + } + if (isWrapped(HTMLElement.prototype.removeEventListener)) { + this._unwrap(HTMLElement.prototype, 'removeEventListener'); + } } this._unpatchHistoryApi(); } diff --git a/plugins/web/opentelemetry-plugin-user-interaction/src/version.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/src/version.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/src/version.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/src/version.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/test/helper.test.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/test/helper.test.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/test/helper.test.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/test/helper.test.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/test/index-webpack.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/test/index-webpack.ts similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/test/index-webpack.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/test/index-webpack.ts diff --git a/plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.nozone.test.ts similarity index 94% rename from plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.nozone.test.ts index 65ced81285..b03ee415ec 100644 --- a/plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts +++ b/plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.nozone.test.ts @@ -15,15 +15,14 @@ */ const originalSetTimeout = window.setTimeout; -import { context } from '@opentelemetry/api'; import { isWrapped, LogLevel } from '@opentelemetry/core'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; -import { ZoneContextManager } from '@opentelemetry/context-zone-peer-dep'; import * as tracing from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; import * as assert from 'assert'; import * as sinon from 'sinon'; -import { UserInteractionPlugin } from '../src'; +import { UserInteractionInstrumentation } from '../src'; import { assertClickSpan, DummySpanExporter, @@ -34,18 +33,15 @@ import { const FILE_URL = 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/main/package.json'; -describe('UserInteractionPlugin', () => { +describe('UserInteractionInstrumentation', () => { describe('when zone.js is NOT available', () => { - let contextManager: ZoneContextManager; - let userInteractionPlugin: UserInteractionPlugin; + let userInteractionInstrumentation: UserInteractionInstrumentation; let sandbox: sinon.SinonSandbox; let webTracerProvider: WebTracerProvider; let dummySpanExporter: DummySpanExporter; let exportSpy: sinon.SinonSpy; let requests: sinon.SinonFakeXMLHttpRequest[] = []; beforeEach(() => { - contextManager = new ZoneContextManager().enable(); - context.setGlobalContextManager(contextManager); sandbox = sinon.createSandbox(); const fakeXhr = sandbox.useFakeXMLHttpRequest(); fakeXhr.onCreate = function (xhr: sinon.SinonFakeXMLHttpRequest) { @@ -61,15 +57,18 @@ describe('UserInteractionPlugin', () => { sandbox.useFakeTimers(); - userInteractionPlugin = new UserInteractionPlugin(); + userInteractionInstrumentation = new UserInteractionInstrumentation({ + enabled: false, + }); - sinon - .stub(userInteractionPlugin, 'getZoneWithPrototype') - .callsFake(() => undefined); + sandbox + .stub(userInteractionInstrumentation, 'getZoneWithPrototype') + .callsFake(() => { + return false as any; + }); webTracerProvider = new WebTracerProvider({ logLevel: LogLevel.ERROR, - plugins: [userInteractionPlugin, new XMLHttpRequestInstrumentation()], }); dummySpanExporter = new DummySpanExporter(); @@ -77,6 +76,15 @@ describe('UserInteractionPlugin', () => { webTracerProvider.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); + webTracerProvider.register(); + + registerInstrumentations({ + tracerProvider: webTracerProvider, + instrumentations: [ + userInteractionInstrumentation, + new XMLHttpRequestInstrumentation(), + ], + }); // this is needed as window is treated as context and karma is adding // context which is then detected as spanContext @@ -86,7 +94,6 @@ describe('UserInteractionPlugin', () => { requests = []; sandbox.restore(); exportSpy.restore(); - context.disable(); }); it('should not break removeEventListener', () => { @@ -242,7 +249,7 @@ describe('UserInteractionPlugin', () => { }); it('should not create span when start span fails', done => { - userInteractionPlugin['_tracer'].startSpan = function () { + userInteractionInstrumentation['_tracer'].startSpan = function () { throw 'foo'; }; @@ -404,7 +411,7 @@ describe('UserInteractionPlugin', () => { }); }); - it('should handle unpatch', () => { + it('should handle disable', () => { assert.strictEqual( isWrapped(HTMLElement.prototype.addEventListener), true, @@ -438,7 +445,7 @@ describe('UserInteractionPlugin', () => { ); assert.strictEqual(isWrapped(history.go), true, 'go should be wrapped'); - userInteractionPlugin.disable(); + userInteractionInstrumentation.disable(); assert.strictEqual( isWrapped(HTMLElement.prototype.addEventListener), diff --git a/plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts b/plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.test.ts similarity index 93% rename from plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts rename to plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.test.ts index b0f0f482d7..e71494f43d 100644 --- a/plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.test.ts +++ b/plugins/web/opentelemetry-instrumentation-user-interaction/test/userInteraction.test.ts @@ -17,14 +17,18 @@ const originalSetTimeout = window.setTimeout; import { context } from '@opentelemetry/api'; import { ROOT_CONTEXT } from '@opentelemetry/context-base'; import { ZoneContextManager } from '@opentelemetry/context-zone-peer-dep'; -import { isWrapped, LogLevel } from '@opentelemetry/core'; +import { + isWrapped, + registerInstrumentations, +} from '@opentelemetry/instrumentation'; +import { LogLevel } from '@opentelemetry/core'; import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; import * as tracing from '@opentelemetry/tracing'; import { WebTracerProvider } from '@opentelemetry/web'; import * as assert from 'assert'; import * as sinon from 'sinon'; import 'zone.js'; -import { UserInteractionPlugin } from '../src'; +import { UserInteractionInstrumentation } from '../src'; import { WindowWithZone } from '../src/types'; import { assertClickSpan, @@ -37,10 +41,10 @@ import { const FILE_URL = 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/main/package.json'; -describe('UserInteractionPlugin', () => { +describe('UserInteractionInstrumentation', () => { describe('when zone.js is available', () => { let contextManager: ZoneContextManager; - let userInteractionPlugin: UserInteractionPlugin; + let userInteractionInstrumentation: UserInteractionInstrumentation; let sandbox: sinon.SinonSandbox; let webTracerProvider: WebTracerProvider; let dummySpanExporter: DummySpanExporter; @@ -48,7 +52,6 @@ describe('UserInteractionPlugin', () => { let requests: sinon.SinonFakeXMLHttpRequest[] = []; beforeEach(() => { contextManager = new ZoneContextManager().enable(); - context.setGlobalContextManager(contextManager); sandbox = sinon.createSandbox(); history.pushState({ test: 'testing' }, '', `${location.pathname}`); const fakeXhr = sandbox.useFakeXMLHttpRequest(); @@ -65,10 +68,8 @@ describe('UserInteractionPlugin', () => { sandbox.useFakeTimers(); - userInteractionPlugin = new UserInteractionPlugin(); webTracerProvider = new WebTracerProvider({ logLevel: LogLevel.ERROR, - plugins: [userInteractionPlugin, new XMLHttpRequestInstrumentation()], }); dummySpanExporter = new DummySpanExporter(); exportSpy = sandbox.stub(dummySpanExporter, 'export'); @@ -76,6 +77,19 @@ describe('UserInteractionPlugin', () => { new tracing.SimpleSpanProcessor(dummySpanExporter) ); + webTracerProvider.register({ + contextManager, + }); + userInteractionInstrumentation = new UserInteractionInstrumentation(); + + registerInstrumentations({ + tracerProvider: webTracerProvider, + instrumentations: [ + userInteractionInstrumentation, + new XMLHttpRequestInstrumentation(), + ], + }); + // this is needed as window is treated as context and karma is adding // context which is then detected as spanContext (window as { context?: {} }).context = undefined; @@ -109,9 +123,7 @@ describe('UserInteractionPlugin', () => { it('should ignore periodic tasks', done => { fakeInteraction(() => { - const interval = setInterval(() => { - // console.log('interval ....'); - }, 1); + const interval = setInterval(() => {}, 1); originalSetTimeout(() => { assert.equal( exportSpy.args.length, @@ -346,7 +358,7 @@ describe('UserInteractionPlugin', () => { ); assert.strictEqual(isWrapped(history.go), true, 'go should be wrapped'); - userInteractionPlugin.disable(); + userInteractionInstrumentation.disable(); assert.strictEqual( isWrapped(ZoneWithPrototype.prototype.runTask), diff --git a/plugins/web/opentelemetry-plugin-user-interaction/tsconfig.json b/plugins/web/opentelemetry-instrumentation-user-interaction/tsconfig.json similarity index 100% rename from plugins/web/opentelemetry-plugin-user-interaction/tsconfig.json rename to plugins/web/opentelemetry-instrumentation-user-interaction/tsconfig.json