diff --git a/.github/component_owners.yml b/.github/component_owners.yml
index ed94624ee2..e9d657e9d8 100644
--- a/.github/component_owners.yml
+++ b/.github/component_owners.yml
@@ -44,6 +44,9 @@ components:
- rauno56
plugins/web/opentelemetry-instrumentation-document-load:
- obecny
+ plugins/web/opentelemetry-instrumentation-long-task:
+ - mhennoch
+ - t2t2
plugins/web/opentelemetry-instrumentation-user-interaction:
- obecny
propagators/opentelemetry-propagator-aws-xray:
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/.eslintignore b/plugins/web/opentelemetry-instrumentation-long-task/.eslintignore
new file mode 100644
index 0000000000..378eac25d3
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/.eslintignore
@@ -0,0 +1 @@
+build
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/.eslintrc.js b/plugins/web/opentelemetry-instrumentation-long-task/.eslintrc.js
new file mode 100644
index 0000000000..6ad6c9f216
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/.eslintrc.js
@@ -0,0 +1,9 @@
+module.exports = {
+ "env": {
+ "mocha": true,
+ "commonjs": true,
+ "browser": true,
+ "jquery": true
+ },
+ ...require('../../../eslint.config.js')
+}
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/README.md b/plugins/web/opentelemetry-instrumentation-long-task/README.md
new file mode 100644
index 0000000000..6a6659ee97
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/README.md
@@ -0,0 +1,59 @@
+# OpenTelemetry Long Task Instrumentation for web
+
+[![NPM Published Version][npm-img]][npm-url]
+[![dependencies][dependencies-image]][dependencies-url]
+[![devDependencies][devDependencies-image]][devDependencies-url]
+[![Apache License][license-image]][license-image]
+
+This instrumentation creates spans from tasks that take more than 50 milliseconds using the [Long Task API][mdn-long-task].
+All of the data reported via [`PerformanceLongTaskTiming`][mdn-performance-long-task-timing] is included as span attributes.
+
+Compatible with OpenTelemetry JS API and SDK `1.0+`.
+
+## Installation
+
+```bash
+npm install --save @opentelemetry/instrumentation-long-task
+```
+
+## Usage
+
+```js
+import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
+import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
+import { LongTaskInstrumentation } from '@opentelemetry/instrumentation-long-task';
+import { registerInstrumentations } from '@opentelemetry/instrumentation';
+
+const provider = new WebTracerProvider();
+
+provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
+
+registerInstrumentations({
+ tracerProvider: provider,
+ instrumentations: [
+ new LongTaskInstrumentation(),
+ ],
+});
+```
+
+## 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-contrib.svg?path=plugins%2Fweb%2Fopentelemetry-instrumentation-long-task
+[dependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=plugins%2Fweb%2Fopentelemetry-instrumentation-long-task
+[devDependencies-image]: https://status.david-dm.org/gh/open-telemetry/opentelemetry-js-contrib.svg?path=plugins%2Fweb%2Fopentelemetry-instrumentation-long-task&type=dev
+[devDependencies-url]: https://david-dm.org/open-telemetry/opentelemetry-js-contrib?path=plugins%2Fweb%2Fopentelemetry-instrumentation-long-task&type=dev
+[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-long-task
+[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-long-task.svg
+[mdn-long-task]: https://developer.mozilla.org/en-US/docs/Web/API/Long_Tasks_API
+[mdn-performance-long-task-timing]: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceLongTaskTiming
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/karma.conf.js b/plugins/web/opentelemetry-instrumentation-long-task/karma.conf.js
new file mode 100644
index 0000000000..4a4bd49791
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/karma.conf.js
@@ -0,0 +1,25 @@
+/*!
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ */
+
+const karmaWebpackConfig = require('../../../karma.webpack');
+const karmaBaseConfig = require('../../../karma.base');
+
+module.exports = (config) => {
+ config.set(Object.assign({}, karmaBaseConfig, {
+ frameworks: karmaBaseConfig.frameworks.concat(['jquery-1.8.3']),
+ webpack: karmaWebpackConfig,
+ }))
+};
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/package.json b/plugins/web/opentelemetry-instrumentation-long-task/package.json
new file mode 100644
index 0000000000..98454993bd
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/package.json
@@ -0,0 +1,91 @@
+{
+ "name": "@opentelemetry/instrumentation-long-task",
+ "version": "0.27.0",
+ "description": "OpenTelemetry long task API automatic instrumentation package.",
+ "main": "build/src/index.js",
+ "module": "build/esm/index.js",
+ "types": "build/src/index.d.ts",
+ "repository": "open-telemetry/opentelemetry-js-contrib",
+ "scripts": {
+ "lint": "eslint . --ext .ts",
+ "lint:fix": "eslint . --ext .ts --fix",
+ "clean": "tsc --build --clean tsconfig.json tsconfig.esm.json",
+ "codecov:browser": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
+ "precompile": "tsc --version && lerna run version --scope @opentelemetry/instrumentation-user-interaction --include-filtered-dependencies",
+ "prewatch": "npm run precompile",
+ "version:update": "node ../../../scripts/version-update.js",
+ "compile": "npm run version:update && tsc --build tsconfig.json tsconfig.esm.json",
+ "prepare": "npm run compile",
+ "tdd": "karma start",
+ "test:browser": "nyc karma start --single-run",
+ "watch": "tsc --build --watch tsconfig.json tsconfig.esm.json"
+ },
+ "keywords": [
+ "opentelemetry",
+ "web",
+ "tracing",
+ "profiling",
+ "metrics",
+ "stats"
+ ],
+ "author": "OpenTelemetry Authors",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "files": [
+ "build/esm/**/*.js",
+ "build/esm/**/*.map",
+ "build/esm/**/*.d.ts",
+ "build/src/**/*.js",
+ "build/src/**/*.map",
+ "build/src/**/*.d.ts",
+ "doc",
+ "LICENSE",
+ "README.md"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "devDependencies": {
+ "@babel/core": "7.15.0",
+ "@opentelemetry/api": "1.0.2",
+ "@opentelemetry/sdk-trace-base": "1.0.1",
+ "@types/jquery": "3.5.6",
+ "@types/mocha": "7.0.2",
+ "@types/node": "14.17.9",
+ "@types/sinon": "10.0.2",
+ "@types/webpack-env": "1.16.2",
+ "babel-loader": "8.2.2",
+ "codecov": "3.8.3",
+ "gts": "3.1.0",
+ "istanbul-instrumenter-loader": "3.0.1",
+ "karma": "5.2.3",
+ "karma-chrome-launcher": "3.1.0",
+ "karma-coverage-istanbul-reporter": "3.0.3",
+ "karma-jquery": "0.2.4",
+ "karma-mocha": "2.0.1",
+ "karma-spec-reporter": "0.0.32",
+ "karma-webpack": "4.0.2",
+ "mocha": "7.2.0",
+ "nyc": "15.1.0",
+ "rimraf": "3.0.2",
+ "sinon": "11.1.2",
+ "ts-loader": "8.3.0",
+ "ts-mocha": "8.0.0",
+ "typescript": "4.3.5",
+ "webpack": "4.46.0",
+ "webpack-cli": "4.7.2",
+ "webpack-merge": "5.8.0",
+ "zone.js": "0.11.4"
+ },
+ "dependencies": {
+ "@opentelemetry/core": "^1.0.0",
+ "@opentelemetry/instrumentation": "^0.27.0",
+ "@opentelemetry/sdk-trace-web": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.0.2"
+ },
+ "sideEffects": false
+}
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/src/index.ts b/plugins/web/opentelemetry-instrumentation-long-task/src/index.ts
new file mode 100644
index 0000000000..24c76056a1
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/src/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export * from './instrumentation';
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/src/instrumentation.ts b/plugins/web/opentelemetry-instrumentation-long-task/src/instrumentation.ts
new file mode 100644
index 0000000000..1bebc4d70c
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/src/instrumentation.ts
@@ -0,0 +1,135 @@
+/*
+ * 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 { hrTime } from '@opentelemetry/core';
+import {
+ InstrumentationBase,
+ InstrumentationConfig,
+} from '@opentelemetry/instrumentation';
+import { VERSION } from './version';
+
+// Currently missing in typescript DOM definitions
+interface PerformanceLongTaskTiming extends PerformanceEntry {
+ attribution: TaskAttributionTiming[];
+}
+
+interface TaskAttributionTiming extends PerformanceEntry {
+ containerType: string;
+ containerSrc: string;
+ containerId: string;
+ containerName: string;
+}
+
+const LONGTASK_PERFORMANCE_TYPE = 'longtask';
+
+export class LongTaskInstrumentation extends InstrumentationBase {
+ readonly component: string = 'long-task';
+ readonly version: string = VERSION;
+ moduleName = this.component;
+
+ private _observer?: PerformanceObserver;
+
+ /**
+ *
+ * @param config
+ */
+ constructor(config: InstrumentationConfig = {}) {
+ super('@opentelemetry/instrumentation-long-task', VERSION, config);
+ }
+
+ init() {}
+
+ private isSupported() {
+ if (
+ typeof PerformanceObserver === 'undefined' ||
+ !PerformanceObserver.supportedEntryTypes
+ ) {
+ return false;
+ }
+
+ return PerformanceObserver.supportedEntryTypes.includes(
+ LONGTASK_PERFORMANCE_TYPE
+ );
+ }
+
+ private _createSpanFromEntry(entry: PerformanceLongTaskTiming) {
+ const span = this.tracer.startSpan(LONGTASK_PERFORMANCE_TYPE, {
+ startTime: hrTime(entry.startTime),
+ });
+ span.setAttribute('component', this.component);
+ span.setAttribute('longtask.name', entry.name);
+ span.setAttribute('longtask.entry_type', entry.entryType);
+ span.setAttribute('longtask.duration', entry.duration);
+
+ if (Array.isArray(entry.attribution)) {
+ entry.attribution.forEach((attribution, index) => {
+ const prefix =
+ entry.attribution.length > 1
+ ? `longtask.attribution[${index}]`
+ : 'longtask.attribution';
+ span.setAttribute(`${prefix}.name`, attribution.name);
+ span.setAttribute(`${prefix}.entry_type`, attribution.entryType);
+ span.setAttribute(`${prefix}.start_time`, attribution.startTime);
+ span.setAttribute(`${prefix}.duration`, attribution.duration);
+ span.setAttribute(
+ `${prefix}.container_type`,
+ attribution.containerType
+ );
+ span.setAttribute(`${prefix}.container_src`, attribution.containerSrc);
+ span.setAttribute(`${prefix}.container_id`, attribution.containerId);
+ span.setAttribute(
+ `${prefix}.container_name`,
+ attribution.containerName
+ );
+ });
+ }
+
+ span.end(hrTime(entry.startTime + entry.duration));
+ }
+
+ override enable() {
+ if (!this.isSupported()) {
+ this._diag.debug('Environment not supported');
+ return;
+ }
+
+ if (this._observer) {
+ // Already enabled
+ return;
+ }
+
+ this._observer = new PerformanceObserver(list => {
+ list
+ .getEntries()
+ .forEach(entry =>
+ this._createSpanFromEntry(entry as PerformanceLongTaskTiming)
+ );
+ });
+ this._observer.observe({
+ type: LONGTASK_PERFORMANCE_TYPE,
+ buffered: true,
+ });
+ }
+
+ override disable() {
+ if (!this._observer) {
+ return;
+ }
+
+ this._observer.disconnect();
+ this._observer = undefined;
+ }
+}
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/test/compatibility.test.ts b/plugins/web/opentelemetry-instrumentation-long-task/test/compatibility.test.ts
new file mode 100644
index 0000000000..d3404d19ae
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/test/compatibility.test.ts
@@ -0,0 +1,122 @@
+/*
+ * 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 { trace } from '@opentelemetry/api';
+import { registerInstrumentations } from '@opentelemetry/instrumentation';
+import * as tracing from '@opentelemetry/sdk-trace-base';
+import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
+import { LongTaskInstrumentation } from '../src';
+import { DummySpanExporter } from './util';
+
+/* eslint-disable node/no-unsupported-features/es-builtins */
+const _globalThis: typeof globalThis =
+ typeof globalThis === 'object'
+ ? globalThis
+ : typeof self === 'object'
+ ? self
+ : typeof window === 'object'
+ ? window
+ : typeof global === 'object'
+ ? global
+ : ({} as typeof globalThis);
+/* eslint-enable node/no-unsupported-features/es-builtins */
+
+describe("LongTaskInstrumentation doesn't throw in unsupported environments", () => {
+ let webTracerProvider: WebTracerProvider;
+ let dummySpanExporter: DummySpanExporter;
+
+ before(() => {
+ webTracerProvider = new WebTracerProvider();
+ dummySpanExporter = new DummySpanExporter();
+ webTracerProvider.addSpanProcessor(
+ new tracing.SimpleSpanProcessor(dummySpanExporter)
+ );
+ webTracerProvider.register();
+ });
+
+ after(() => {
+ trace.disable();
+ });
+
+ // Do cleanup for environment changes here so even with test fails won't affect other tests
+ const perfObsDesc = Object.getOwnPropertyDescriptor(
+ _globalThis,
+ 'PerformanceObserver'
+ );
+ const supportedDesc = Object.getOwnPropertyDescriptor(
+ PerformanceObserver,
+ 'supportedEntryTypes'
+ );
+ afterEach(() => {
+ Object.defineProperty(_globalThis, 'PerformanceObserver', perfObsDesc!);
+ Object.defineProperty(
+ PerformanceObserver,
+ 'supportedEntryTypes',
+ supportedDesc!
+ );
+ });
+
+ // tests based on different browser targets in
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver#browser_compatibility
+ it('No support for PerformanceObserver', async () => {
+ // @ts-expect-error Non-optional in types
+ delete _globalThis.PerformanceObserver;
+
+ const longTaskInstrumentation = new LongTaskInstrumentation({
+ enabled: false,
+ });
+
+ const deregister = registerInstrumentations({
+ instrumentations: [longTaskInstrumentation],
+ });
+
+ deregister();
+ });
+
+ it('No supportedEntryTypes', async () => {
+ // @ts-expect-error Non-optional in types
+ delete PerformanceObserver.supportedEntryTypes;
+
+ const longTaskInstrumentation = new LongTaskInstrumentation({
+ enabled: false,
+ });
+
+ const deregister = registerInstrumentations({
+ instrumentations: [longTaskInstrumentation],
+ });
+
+ deregister();
+ });
+
+ it('longtask not supported', async () => {
+ Object.defineProperty(PerformanceObserver, 'supportedEntryTypes', {
+ get() {
+ return [];
+ },
+ configurable: true,
+ enumerable: false,
+ });
+
+ const longTaskInstrumentation = new LongTaskInstrumentation({
+ enabled: false,
+ });
+
+ const deregister = registerInstrumentations({
+ instrumentations: [longTaskInstrumentation],
+ });
+
+ deregister();
+ });
+});
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/test/index-webpack.ts b/plugins/web/opentelemetry-instrumentation-long-task/test/index-webpack.ts
new file mode 100644
index 0000000000..061a48ccfa
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/test/index-webpack.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const testsContext = require.context('.', true, /test$/);
+testsContext.keys().forEach(testsContext);
+
+const srcContext = require.context('.', true, /src$/);
+srcContext.keys().forEach(srcContext);
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/test/longTask.test.ts b/plugins/web/opentelemetry-instrumentation-long-task/test/longTask.test.ts
new file mode 100644
index 0000000000..fabb02e469
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/test/longTask.test.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { trace } from '@opentelemetry/api';
+import { hrTimeToMilliseconds, hrTimeToNanoseconds } from '@opentelemetry/core';
+import { registerInstrumentations } from '@opentelemetry/instrumentation';
+import * as tracing from '@opentelemetry/sdk-trace-base';
+import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
+import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
+import * as assert from 'assert';
+import * as sinon from 'sinon';
+import { LongTaskInstrumentation } from '../src';
+import { DummySpanExporter } from './util';
+
+const LONGTASK_DURATION = 50 * 1.1; // add 10% margin
+function generateLongTask() {
+ const startingTimestamp = performance.now();
+ while (performance.now() - startingTimestamp < LONGTASK_DURATION) {}
+}
+
+describe('LongTaskInstrumentation', () => {
+ let longTaskInstrumentation: LongTaskInstrumentation;
+ let sandbox: sinon.SinonSandbox;
+ let webTracerProvider: WebTracerProvider;
+ let dummySpanExporter: DummySpanExporter;
+ let exportSpy: sinon.SinonStub;
+ let deregister: () => void;
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ webTracerProvider = new WebTracerProvider();
+ dummySpanExporter = new DummySpanExporter();
+ exportSpy = sandbox.stub(dummySpanExporter, 'export');
+ webTracerProvider.addSpanProcessor(
+ new tracing.SimpleSpanProcessor(dummySpanExporter)
+ );
+ webTracerProvider.register();
+
+ longTaskInstrumentation = new LongTaskInstrumentation({
+ enabled: false,
+ });
+
+ deregister = registerInstrumentations({
+ instrumentations: [longTaskInstrumentation],
+ });
+ });
+
+ afterEach(() => {
+ deregister();
+ sandbox.restore();
+ exportSpy.restore();
+ trace.disable();
+ });
+
+ it('should report long taking tasks', async () => {
+ let taskStart: number;
+ await new Promise(resolve => {
+ // Resolve promise when export gets called
+ exportSpy.callsFake(() => {
+ if (!taskStart) {
+ // Hasn't generated expected longtask yet
+ return;
+ }
+ resolve();
+ });
+ setTimeout(() => {
+ // Cleanup any past longtasks
+ exportSpy.resetHistory();
+ taskStart = performance.timeOrigin + performance.now();
+ generateLongTask();
+ }, 1);
+ });
+
+ assert.strictEqual(exportSpy.args.length, 1, 'should export once');
+ assert.strictEqual(
+ exportSpy.args[0][0].length,
+ 1,
+ 'should export one span'
+ );
+ const span: ReadableSpan = exportSpy.args[0][0][0];
+
+ // Span should match expected longtask timings
+ assert.ok(
+ hrTimeToMilliseconds(span.startTime) <= Math.ceil(taskStart!),
+ 'span start should be when task started'
+ );
+ assert.ok(
+ hrTimeToNanoseconds(span.duration) > LONGTASK_DURATION,
+ "span duration should be longtask's"
+ );
+ });
+});
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/test/util.ts b/plugins/web/opentelemetry-instrumentation-long-task/test/util.ts
new file mode 100644
index 0000000000..ce8179e5ae
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/test/util.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as tracing from '@opentelemetry/sdk-trace-base';
+
+export class DummySpanExporter implements tracing.SpanExporter {
+ export(spans: tracing.ReadableSpan[]) {}
+
+ shutdown() {
+ return Promise.resolve();
+ }
+}
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.esm.json b/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.esm.json
new file mode 100644
index 0000000000..d903fa3086
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.esm.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../../tsconfig.base.esm.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "build/esm",
+ "tsBuildInfoFile": "build/esm/tsconfig.esm.tsbuildinfo"
+ },
+ "files": [ "node_modules/zone.js/dist/zone.js.d.ts"],
+ "include": [
+ "src/**/*.ts"
+ ]
+}
diff --git a/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.json b/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.json
new file mode 100644
index 0000000000..28be80d266
--- /dev/null
+++ b/plugins/web/opentelemetry-instrumentation-long-task/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../../../tsconfig.base",
+ "compilerOptions": {
+ "rootDir": ".",
+ "outDir": "build"
+ },
+ "include": [
+ "src/**/*.ts",
+ "test/**/*.ts"
+ ]
+}