diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4e95e669..cfe3e24c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,11 @@
default:
image: 'cimg/node:current-browsers'
+include:
+ - project: 'ci-cd/templates'
+ ref: master
+ file: '/prodsec/.oss-scan.yml'
+
cache:
key:
files:
@@ -67,6 +72,10 @@ unit_test:
script:
- npm run test:unit:ci
+oss-scan:
+ stage: test
+ extends: .oss-scan
+
release_production:
artifacts:
paths:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db900546..ccacafb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,21 @@ If the version of Open Telemetry is unspecified for a version, then it is the sa
## Unreleased
+## 0.12.1
+
+- Add app version configuration option ([#419](https://github.com/signalfx/splunk-otel-js-web/pull/419))
+- Add http method to {document,resource}Fetch spans ([#424](https://github.com/signalfx/splunk-otel-js-web/pull/424))
+- Filter out invalid CORS network timings ([#422](https://github.com/signalfx/splunk-otel-js-web/pull/422))
+
+## 0.12.0
+
+- make SplunkPostDocLoadResourceInstrumentation aware of upstream context ([#398](https://github.com/signalfx/splunk-otel-js-web/pull/398))
+- Graduate experimental APIs ([#403](https://github.com/signalfx/splunk-otel-js-web/pull/403))
+
+## 0.11.4
+
+- add ignoreUrls config in docload instrumentation ([#392](https://github.com/signalfx/splunk-otel-js-web/pull/392))
+
## 0.11.3
- Fix polyfilled fetch in IE ([#383](https://github.com/signalfx/splunk-otel-js-web/pull/383))
@@ -14,7 +29,7 @@ If the version of Open Telemetry is unspecified for a version, then it is the sa
## 0.11.1
-- Hotfix: Fix event listeners throwing when useCapture = null ([#374](https://github.com/signalfx/splunk-otel-js-web/pull/374))
+- Hotfix: Fix event listeners throwing when useCapture = null ([#374](https://github.com/signalfx/splunk-otel-js-web/pull/374))
## 0.11.0
diff --git a/docs/API.md b/docs/API.md
new file mode 100644
index 00000000..dfa70b88
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,107 @@
+When using the API with CDN installation, usage of try...catch blocks is encouraged to not break your app in case RUM library isn't loaded for some reason. This does not apply when bundling RUM with your application (using a npm installation).
+
+# setGlobalAttributes
+
+```ts
+SplunkRum.setGlobalAttributes(attributes?: Attributes): void;
+```
+
+Update attributes that will be set on every new span. For example this can be used to [update user information on logging in](./IdentifyingUsers.md#providing-it-after-initialisation-using-api)
+
+* `attributes`: [Attributes](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/attributes.html) - Object of attributes that will be set on every span. If undefined, all currently set global attributes will be deleted and no longer added to any new spans.
+
+Returns nothing
+
+**Example:**
+
+```js
+SplunkRum.setGlobalAttributes({
+ 'enduser.id': 'Test User',
+});
+// All new spans now include enduser.id
+
+SplunkRum.setGlobalAttributes({
+ 'dark_mode.enabled': darkModeToggle.status,
+});
+// All new spans now include enduser.id and dark_mode.enabled
+
+SplunkRum.setGlobalAttributes()
+// New spans no longer include those attributes
+```
+
+# getGlobalAttributes
+
+```ts
+SplunkRum.getGlobalAttributes(): Attributes;
+```
+
+Get current set global attributes
+
+No parameters
+
+Returns [Attributes](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/attributes.html) object with attributes that are set on all new spans
+
+**Example:**
+
+```js
+SplunkRum.setGlobalAttributes({
+ 'enduser.id': 'Test User',
+});
+SplunkRum.setGlobalAttributes({
+ 'dark_mode.enabled': darkModeToggle.status,
+});
+
+const attrs = SplunkRum.getGlobalAttributes();
+/* console.log(attrs)
+{
+ 'enduser.id': 'Test User',
+ 'dark_mode.enabled': true
+}
+*/
+```
+
+# getSessionId
+
+```ts
+SplunkRum.getSessionId(): string;
+```
+
+Get current session ID
+
+No parameters
+
+Returns string
+
+**Example:**
+
+```js
+LiveChat.onChatStarted(() => {
+ LiveChat.setMetadata('splunk.sessionId', SplunkRum.getSessionId());
+});
+```
+
+# Events
+
+Event listeners can be registered with `addEventListener` and removed with `removeEventListener`. Event listeners receive an object `{ payload: { /* Depending on event */ }}` as the first parameter
+
+```ts
+SplunkRum.addEventListener(type: string, listener: Function): void
+SplunkRum.removeEventListener(type: string, listener: Function): void
+```
+
+Events:
+
+* `'session-changed'` - Emitted when session ID changes
+ * Payload:
+ * `sessionId`: string - New session ID
+* `'global-attributes-changed'` - Emitted when setGlobalAttributes is called
+ * Payload:
+ * `attributes`: [Attributes](https://open-telemetry.github.io/opentelemetry-js-api/interfaces/attributes.html) - New global attributes
+
+**Example:**
+
+```js
+SplunkRum.addEventListener('session-changed', (event) => {
+ LiveChat.setMetadata('splunk.sessionId', event.payload.sessionId);
+});
+```
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 12d93d32..4c5b57ab 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -12,6 +12,7 @@ Below are the different initialization options available for the Agent:
|`rumAuth`|`string [required]`|Provided by installation wizard|Defines a token authorizing the Agent to send the telemetry to the backend. You can find (or generate) the token [here](https://app.signalfx.com/o11y/#/organization/current?selectedKeyValue=sf_section:accesstokens). Notice that RUM and APM auth tokens are different.|
|`app`|`string`|`"unknown-browser-app"`|Application name, used to distinguish the telemetry from different applications.|
|`environment`|`string`|`(none)`|Sets environment for all the spans, used to distinguish between different environments such as `dev`, `test` or `prod`. |
+|`version`|`string`|`(none)`|Sets app version for all the spans, used to distinguish between different version deployments. For example: `1.0.1` or `20220820` |
|`globalAttributes`|`object`|`{} empty object`|Sets additional attributes added to all spans (such as version, user id, ...)|
|`allowInsecureBeacon`|`boolean`|`false`|Allows sending data to insecure endpoints not using `https`. It is not recommended to enable this. |
|`debug`|`boolean`|`false`|Enables debug logging in developer console|
diff --git a/integration-tests/otel-api-globals.ts b/integration-tests/otel-api-globals.ts
new file mode 100644
index 00000000..1759c67e
--- /dev/null
+++ b/integration-tests/otel-api-globals.ts
@@ -0,0 +1,4 @@
+/* eslint-disable header/header */
+import { trace, context } from '@opentelemetry/api';
+
+export { trace, context };
\ No newline at end of file
diff --git a/integration-tests/tests/cdn/index.spec.js b/integration-tests/tests/cdn/index.spec.js
index 2e0f0555..693e1fc6 100644
--- a/integration-tests/tests/cdn/index.spec.js
+++ b/integration-tests/tests/cdn/index.spec.js
@@ -49,7 +49,7 @@ module.exports = {
}
const rumScriptFetchSpan = await browser.globals.findSpan(
- (s) => s.name === 'resourceFetch'
+ (s) => s.name === 'resourceFetch' && s.tags['http.url'].includes('cdn.signalfx.com')
);
await browser.assert.ok(
!!rumScriptFetchSpan,
@@ -64,7 +64,7 @@ module.exports = {
);
await browser.assert.strictEqual(
rumScriptFetchSpan.tags['splunk.rumVersion'],
- '0.11.2'
+ '0.12.1'
);
},
};
diff --git a/integration-tests/tests/docload/docload-ignored.ejs b/integration-tests/tests/docload/docload-ignored.ejs
new file mode 100644
index 00000000..30f9c7cb
--- /dev/null
+++ b/integration-tests/tests/docload/docload-ignored.ejs
@@ -0,0 +1,19 @@
+
+
+
+
+ Test init
+
+ <%- renderAgent({}, true) %>
+
+
+
+ Docload
+
+
+
diff --git a/integration-tests/tests/docload/docload.spec.js b/integration-tests/tests/docload/docload.spec.js
index 33c53eb3..d8379586 100644
--- a/integration-tests/tests/docload/docload.spec.js
+++ b/integration-tests/tests/docload/docload.spec.js
@@ -79,6 +79,15 @@ module.exports = {
await browser.globals.assertNoErrorSpans();
},
+ 'ignoring resource URLs': async function(browser) {
+ await browser.url(browser.globals.getUrl('/docload/docload-ignored.ejs'));
+
+ await browser.globals.findSpan(span => span.name === 'documentFetch');
+ const url = browser.globals.getUrl('/', []);
+ await browser.assert.not.ok(browser.globals.getReceivedSpans().find(
+ span => span.tags['http.url'] === url + 'non-impactful-resource.jpg'
+ ));
+ },
'module can be disabled': async function(browser) {
await browser.url(browser.globals.getUrl('/docload/docload.ejs?disableInstrumentation=document'));
await browser.globals.waitForTestToFinish();
diff --git a/integration-tests/tests/errors/index.spec.js b/integration-tests/tests/errors/index.spec.js
index 72440771..f8952a28 100644
--- a/integration-tests/tests/errors/index.spec.js
+++ b/integration-tests/tests/errors/index.spec.js
@@ -124,11 +124,11 @@ module.exports = {
await browser.assert.strictEqual(tags['error.message'], ERROR_MESSAGE_MAP[browserName]);
const ERROR_STACK_MAP = {
- safari: `global code@${url}:61:15`,
- chrome: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:61:25`,
- microsoftedge: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:61:25`,
- firefox: `@${url}:61:7\n`,
- 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:61:7)`,
+ safari: `global code@${url}:62:15`,
+ chrome: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`,
+ microsoftedge: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`,
+ firefox: `@${url}:62:7\n`,
+ 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:62:7)`,
};
await browser.assert.strictEqual(tags['error.stack'], ERROR_STACK_MAP[browserName]);
},
@@ -156,11 +156,11 @@ module.exports = {
await browser.assert.strictEqual(tags['error.message'], ERROR_MESSAGE_MAP[browserName]);
const ERROR_STACK_MAP = {
- safari: `global code@${url}:61:15`,
- microsoftedge: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:61:25`,
- chrome: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:61:25`,
- firefox: `@${url}:61:7\n`,
- 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:61:7)`,
+ safari: `global code@${url}:62:15`,
+ microsoftedge: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`,
+ chrome: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:62:25`,
+ firefox: `@${url}:62:7\n`,
+ 'internet explorer': `TypeError: Unable to set property 'anyField' of undefined or null reference\n at Global code (${url}:62:7)`,
};
await browser.assert.strictEqual(tags['error.stack'], ERROR_STACK_MAP[browserName]);
},
diff --git a/integration-tests/tests/errors/views/unhandled-error.ejs b/integration-tests/tests/errors/views/unhandled-error.ejs
index 4aafe87c..2c4af500 100644
--- a/integration-tests/tests/errors/views/unhandled-error.ejs
+++ b/integration-tests/tests/errors/views/unhandled-error.ejs
@@ -5,8 +5,14 @@
Errors test
<%- renderAgent({}) %>
diff --git a/integration-tests/tests/resource-observer/resource-obs.spec.js b/integration-tests/tests/resource-observer/resource-obs.spec.js
index 29fa0301..d423e81c 100644
--- a/integration-tests/tests/resource-observer/resource-obs.spec.js
+++ b/integration-tests/tests/resource-observer/resource-obs.spec.js
@@ -136,5 +136,56 @@ module.exports = {
const afterLoadImage = imageSpans.find( span => span.tags['component'] === 'splunk-post-doc-load-resource');
await browser.assert.notEqual(docLoadImage.traceId, afterLoadImage.traceId);
+ },
+ 'should propagate tracing context to created spans': async function(browser) {
+ if (isBrowser(browser, {
+ safari: true,
+ edge: true,
+ ie: true,
+ })) {
+ return;
+ }
+ await browser.url(browser.globals.getUrl('/resource-observer/resources-custom-context.ejs'));
+ await browser.globals.findSpan(span => span.name === 'guard-span');
+
+ const plImageRootSpan = await browser.globals.getReceivedSpans().find(
+ span => span.tags['http.url'] && span.tags['http.url'].endsWith('splunk-black.png')
+ && span.tags['component'] === 'splunk-post-doc-load-resource'
+ );
+ const plImageParentSpan = await browser.globals.getReceivedSpans().find(
+ span => span.name === 'image-parent'
+ );
+ const plImageChildSpan = await browser.globals.getReceivedSpans().find(
+ span => span.tags['http.url'] && span.tags['http.url'].endsWith('splunk-black.svg')
+ && span.tags['component'] === 'splunk-post-doc-load-resource'
+ );
+ await browser.assert.ok(plImageRootSpan);
+ await browser.assert.ok(plImageParentSpan);
+ await browser.assert.ok(plImageChildSpan);
+
+ await browser.assert.notEqual(plImageRootSpan.traceId, plImageParentSpan.traceId);
+ await browser.assert.not.ok(plImageRootSpan.parentId);
+ await browser.assert.equal(plImageParentSpan.traceId, plImageChildSpan.traceId);
+ await browser.assert.equal(plImageChildSpan.parentId, plImageParentSpan.id);
+
+ const plScriptRootSpan = await browser.globals.getReceivedSpans().find(
+ span => span.tags['http.url'] && span.tags['http.url'].endsWith('test.js')
+ && span.tags['component'] === 'splunk-post-doc-load-resource'
+ );
+ const plScriptParentSpan = await browser.globals.getReceivedSpans().find(
+ span => span.name === 'script-parent'
+ );
+ const plScriptChildSpan = await browser.globals.getReceivedSpans().find(
+ span => span.tags['http.url'] && span.tags['http.url'].endsWith('test-alt.js')
+ && span.tags['component'] === 'splunk-post-doc-load-resource'
+ );
+ await browser.assert.ok(plScriptRootSpan);
+ await browser.assert.ok(plScriptParentSpan);
+ await browser.assert.ok(plScriptChildSpan);
+
+ await browser.assert.notEqual(plScriptRootSpan.traceId, plScriptParentSpan.traceId);
+ await browser.assert.not.ok(plScriptRootSpan.parentId);
+ await browser.assert.equal(plScriptParentSpan.traceId, plScriptChildSpan.traceId);
+ await browser.assert.equal(plScriptChildSpan.parentId, plScriptParentSpan.id);
}
};
diff --git a/integration-tests/tests/resource-observer/resources-custom-context.ejs b/integration-tests/tests/resource-observer/resources-custom-context.ejs
new file mode 100644
index 00000000..30174ee5
--- /dev/null
+++ b/integration-tests/tests/resource-observer/resources-custom-context.ejs
@@ -0,0 +1,72 @@
+
+
+
+
+ Resource observer test
+ <%- renderAgent() %>
+
+
+ Resource observer test
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 0779b932..57b80d13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@splunk/otel-web",
- "version": "0.11.3-rrweb.7",
+ "version": "0.12.1-rrweb.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@splunk/otel-web",
- "version": "0.11.3-rrweb.7",
+ "version": "0.12.1-rrweb.0",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "~7.18.3",
@@ -52,7 +52,7 @@
"browserstack-local": "^1.4.8",
"chai": "^4.3.4",
"chrome-launcher": "^0.15.0",
- "chromedriver": "^101.0.0",
+ "chromedriver": "^103.0.0",
"codecov": "^3.8.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
@@ -4640,12 +4640,13 @@
}
},
"node_modules/axios": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
- "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dev": true,
"dependencies": {
- "follow-redirects": "^1.14.4"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"node_modules/babel-plugin-dynamic-import-node": {
@@ -5340,14 +5341,14 @@
}
},
"node_modules/chromedriver": {
- "version": "101.0.0",
- "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-101.0.0.tgz",
- "integrity": "sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w==",
+ "version": "103.0.0",
+ "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-103.0.0.tgz",
+ "integrity": "sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@testim/chrome-version": "^1.1.2",
- "axios": "^0.24.0",
+ "axios": "^0.27.2",
"del": "^6.0.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "^5.0.0",
@@ -7836,6 +7837,20 @@
"node": ">=8.0.0"
}
},
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -13055,20 +13070,6 @@
"sl": "bin/sl"
}
},
- "node_modules/saucelabs/node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dev": true,
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/saucelabs/node_modules/yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
@@ -18454,12 +18455,13 @@
"dev": true
},
"axios": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
- "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dev": true,
"requires": {
- "follow-redirects": "^1.14.4"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"babel-plugin-dynamic-import-node": {
@@ -18970,13 +18972,13 @@
}
},
"chromedriver": {
- "version": "101.0.0",
- "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-101.0.0.tgz",
- "integrity": "sha512-LkkWxy6KM/0YdJS8qBeg5vfkTZTRamhBfOttb4oic4echDgWvCU1E8QcBbUBOHqZpSrYMyi7WMKmKMhXFUaZ+w==",
+ "version": "103.0.0",
+ "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-103.0.0.tgz",
+ "integrity": "sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w==",
"dev": true,
"requires": {
"@testim/chrome-version": "^1.1.2",
- "axios": "^0.24.0",
+ "axios": "^0.27.2",
"del": "^6.0.0",
"extract-zip": "^2.0.1",
"https-proxy-agent": "^5.0.0",
@@ -20905,6 +20907,17 @@
"signal-exit": "^3.0.2"
}
},
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -24907,17 +24920,6 @@
"yargs": "^17.2.1"
},
"dependencies": {
- "form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- }
- },
"yargs": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
diff --git a/package.json b/package.json
index a0902896..f9cf476b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@splunk/otel-web",
- "version": "0.11.3-rrweb.7",
+ "version": "0.12.1-rrweb.0",
"description": "Splunk distribution of Open Telemetry for browser environment.",
"repository": "github:signalfx/splunk-otel-js-browser",
"scripts": {
@@ -92,7 +92,7 @@
"browserstack-local": "^1.4.8",
"chai": "^4.3.4",
"chrome-launcher": "^0.15.0",
- "chromedriver": "^101.0.0",
+ "chromedriver": "^103.0.0",
"codecov": "^3.8.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
diff --git a/rollup.config.js b/rollup.config.js
index 241617f3..10f6c0b7 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -68,6 +68,33 @@ export default [
],
context: 'window',
},
+ {
+ input: 'integration-tests/otel-api-globals.ts',
+ output: {
+ file: 'dist/artifacts/otel-api-globals.js',
+ format: 'iife',
+ name: 'OtelApiGlobals',
+ sourcemap: true,
+ },
+ plugins: [
+ json(),
+ nodeResolvePlugin,
+ commonjs({
+ include: /node_modules/,
+ sourceMap: true,
+ transformMixedEsModules: true,
+ }),
+ typescript({ tsconfig: './tsconfig.base.json' }),
+ babelPlugin,
+ terser({
+ ecma: 5,
+ output: {
+ comments: false
+ }
+ }),
+ ],
+ context: 'window',
+ },
{
input: 'src/record/index.ts',
output: {
diff --git a/src/EventTarget.ts b/src/EventTarget.ts
index 27a26d2b..b8077307 100644
--- a/src/EventTarget.ts
+++ b/src/EventTarget.ts
@@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { SpanAttributes } from '@opentelemetry/api';
+import { Attributes } from '@opentelemetry/api';
export interface SplunkOtelWebEventTypes {
'session-changed': {
sessionId: string
};
'global-attributes-changed': {
- attributes: SpanAttributes
+ attributes: Attributes
}
}
@@ -46,7 +46,8 @@ export class InternalEventTarget {
const i = (this.events[type] as SplunkEventListener[]).indexOf(listener);
if (i >= 0) {
- this.events[type].splice(i, 1);
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ this.events[type]!.splice(i, 1);
}
}
@@ -66,6 +67,14 @@ export class InternalEventTarget {
export interface SplunkOtelWebEventTarget {
+ addEventListener: InternalEventTarget['addEventListener'];
+ /**
+ * @deprecated Use {@link addEventListener}
+ */
_experimental_addEventListener: InternalEventTarget['addEventListener'];
+ removeEventListener: InternalEventTarget['removeEventListener'];
+ /**
+ * @deprecated Use {@link removeEventListener}
+ */
_experimental_removeEventListener: InternalEventTarget['removeEventListener'];
}
diff --git a/src/SplunkContextManager.ts b/src/SplunkContextManager.ts
index 85280c40..fac46e3f 100644
--- a/src/SplunkContextManager.ts
+++ b/src/SplunkContextManager.ts
@@ -21,6 +21,8 @@ import { getOriginalFunction, isFunction, wrapNatively } from './utils';
export interface ContextManagerConfig {
/** Enable async tracking of span parents */
async?: boolean;
+ onBeforeContextStart?: () => void;
+ onBeforeContextEnd?: () => void;
}
type EventListenerWithOrig = EventListener & {_orig?: EventListener};
@@ -621,13 +623,20 @@ export class SplunkContextManager implements ContextManager {
thisArg?: ThisParameterType,
...args: A
): ReturnType {
+ try {
+ this._config.onBeforeContextStart?.();
+ } catch (e) {
+ // ignore any exceptions thrown by context hooks
+ }
const previousContext = this._currentContext;
this._currentContext = context || ROOT_CONTEXT;
// Observe for location.hash changes (as it isn't a (re)configurable property))
const preLocationHash = location.hash;
try {
- return fn.call(thisArg, ...args);
+ const result = fn.call(thisArg, ...args);
+ this._config.onBeforeContextEnd?.();
+ return result;
} finally {
this._currentContext = previousContext;
if (preLocationHash !== location.hash) {
diff --git a/src/SplunkDocumentLoadInstrumentation.ts b/src/SplunkDocumentLoadInstrumentation.ts
index 6af13dc1..fb43afca 100644
--- a/src/SplunkDocumentLoadInstrumentation.ts
+++ b/src/SplunkDocumentLoadInstrumentation.ts
@@ -20,6 +20,12 @@ import * as api from '@opentelemetry/api';
import { captureTraceParentFromPerformanceEntries } from './servertiming';
import { PerformanceEntries } from '@opentelemetry/sdk-trace-web';
import { Span } from '@opentelemetry/sdk-trace-base';
+import { isUrlIgnored } from '@opentelemetry/core';
+import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
+
+export interface SplunkDocLoadInstrumentationConfig extends InstrumentationConfig {
+ ignoreUrls?: (string|RegExp)[];
+}
const excludedInitiatorTypes = ['beacon', 'fetch', 'xmlhttprequest'];
@@ -40,7 +46,7 @@ type ExposedSuper = {
}
export class SplunkDocumentLoadInstrumentation extends DocumentLoadInstrumentation {
- constructor(config: InstrumentationConfig = {}) {
+ constructor(config: SplunkDocLoadInstrumentationConfig = {}) {
super(config);
const exposedSuper = this as any as ExposedSuper;
@@ -66,6 +72,7 @@ export class SplunkDocumentLoadInstrumentation extends DocumentLoadInstrumentati
}
captureTraceParentFromPerformanceEntries(entries, span);
+ span.setAttribute(SemanticAttributes.HTTP_METHOD, 'GET');
}
if (span && exposedSpan.name === 'documentLoad') {
addExtraDocLoadTags(span);
@@ -75,7 +82,7 @@ export class SplunkDocumentLoadInstrumentation extends DocumentLoadInstrumentati
const _superInitResourceSpan: ExposedSuper['_initResourceSpan'] = exposedSuper._initResourceSpan.bind(this);
exposedSuper._initResourceSpan = (resource, parentSpan) => {
- if (excludedInitiatorTypes.indexOf(resource.initiatorType) !== -1) {
+ if (excludedInitiatorTypes.indexOf(resource.initiatorType) !== -1 || isUrlIgnored(resource.name, config.ignoreUrls)) {
return;
}
_superInitResourceSpan(resource, parentSpan);
diff --git a/src/SplunkExporter.ts b/src/SplunkExporter.ts
index f7f11b30..2c97ff1f 100644
--- a/src/SplunkExporter.ts
+++ b/src/SplunkExporter.ts
@@ -171,6 +171,13 @@ export class SplunkExporter implements SpanExporter {
for (const [key, value] of Object.entries(span.tags)) {
span.tags[key] = limitLen(value.toString(), MAX_VALUE_LIMIT);
}
+ // Remove inaccurate CORS timings
+ const zero = performance.timeOrigin * 1000;
+ if (span.tags['http.url'] && !(span.tags['http.url'] as string).startsWith(location.origin) && span.timestamp > zero && span.annotations) {
+ span.annotations = span.annotations.filter(({ timestamp }) => {
+ return timestamp !== zero;
+ });
+ }
return span;
}
}
diff --git a/src/SplunkPostDocLoadResourceInstrumentation.ts b/src/SplunkPostDocLoadResourceInstrumentation.ts
index fe7745e3..83e3d6d9 100644
--- a/src/SplunkPostDocLoadResourceInstrumentation.ts
+++ b/src/SplunkPostDocLoadResourceInstrumentation.ts
@@ -22,6 +22,7 @@ import { VERSION } from './version';
import { hrTime, isUrlIgnored } from '@opentelemetry/core';
import { addSpanNetworkEvents } from '@opentelemetry/sdk-trace-web';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
+import { context, Context, ROOT_CONTEXT } from '@opentelemetry/api';
export interface SplunkPostDocLoadResourceInstrumentationConfig extends InstrumentationConfig {
allowedInitiatorTypes?: string[];
@@ -30,8 +31,13 @@ export interface SplunkPostDocLoadResourceInstrumentationConfig extends Instrume
const MODULE_NAME = 'splunk-post-doc-load-resource';
const defaultAllowedInitiatorTypes = ['img', 'script']; //other, css, link
+
+const nodeHasSrcAttribute = (node: Node): node is HTMLScriptElement | HTMLImageElement => (node instanceof HTMLScriptElement || node instanceof HTMLImageElement);
+
export class SplunkPostDocLoadResourceInstrumentation extends InstrumentationBase {
- private observer: PerformanceObserver | undefined;
+ private performanceObserver: PerformanceObserver | undefined;
+ private headMutationObserver: MutationObserver | undefined;
+ private urlToContextMap: Record;
private config: SplunkPostDocLoadResourceInstrumentationConfig;
constructor(config: SplunkPostDocLoadResourceInstrumentationConfig = {}) {
@@ -42,6 +48,7 @@ export class SplunkPostDocLoadResourceInstrumentation extends InstrumentationBas
);
super(MODULE_NAME, VERSION, processedConfig);
this.config = processedConfig;
+ this.urlToContextMap = {};
}
init(): void {}
@@ -49,19 +56,29 @@ export class SplunkPostDocLoadResourceInstrumentation extends InstrumentationBas
enable(): void {
if (window.PerformanceObserver) {
window.addEventListener('load', () => {
- this._startObserver();
+ this._startPerformanceObserver();
});
}
+ if (window.MutationObserver) {
+ this._startHeadMutationObserver();
+ }
}
disable(): void {
- if (this.observer) {
- this.observer.disconnect();
+ if (this.performanceObserver) {
+ this.performanceObserver.disconnect();
+ }
+ if (this.headMutationObserver) {
+ this.headMutationObserver.disconnect();
}
}
- private _startObserver() {
- this.observer = new PerformanceObserver((list) => {
+ public onBeforeContextChange(): void {
+ this._processHeadMutationObserverRecords(this.headMutationObserver.takeRecords());
+ }
+
+ private _startPerformanceObserver() {
+ this.performanceObserver = new PerformanceObserver((list) => {
if (window.document.readyState === 'complete') {
list.getEntries().forEach(entry => {
// TODO: check how we can amend TS base typing to fix this
@@ -72,7 +89,31 @@ export class SplunkPostDocLoadResourceInstrumentation extends InstrumentationBas
}
});
//apparently safari 13.1 only supports entryTypes
- this.observer.observe({ entryTypes: ['resource'] });
+ this.performanceObserver.observe({ entryTypes: ['resource'] });
+ }
+
+ private _startHeadMutationObserver() {
+ this.headMutationObserver = new MutationObserver(this._processHeadMutationObserverRecords.bind(this));
+ this.headMutationObserver.observe(document.head, { childList: true });
+ }
+
+ // for each added node that corresponds to a resource load, create an entry in `this.urlToContextMap`
+ // that associates its fully-qualified URL to the tracing context at the time that it was added
+ private _processHeadMutationObserverRecords(mutations: MutationRecord[]) {
+ if (context.active() === ROOT_CONTEXT) {
+ return;
+ }
+ mutations
+ .flatMap(mutation => Array.from(mutation.addedNodes || []))
+ .filter(nodeHasSrcAttribute)
+ .forEach((node) => {
+ const src = node.getAttribute('src');
+ if (!src) {
+ return;
+ }
+ const srcUrl = new URL(src, location.origin);
+ this.urlToContextMap[srcUrl.toString()] = context.active();
+ });
}
// TODO: discuss TS built-in types
@@ -81,16 +122,19 @@ export class SplunkPostDocLoadResourceInstrumentation extends InstrumentationBas
return;
}
+ const targetUrl = new URL(entry.name, location.origin);
const span = this.tracer.startSpan(
//TODO use @opentelemetry/instrumentation-document-load AttributeNames.RESOURCE_FETCH ?,
// AttributeNames not exported currently
'resourceFetch',
{
startTime: hrTime(entry.fetchStart),
- }
+ },
+ this.urlToContextMap[targetUrl.toString()]
);
span.setAttribute('component', MODULE_NAME);
span.setAttribute(SemanticAttributes.HTTP_URL, entry.name);
+ span.setAttribute(SemanticAttributes.HTTP_METHOD, 'GET');
addSpanNetworkEvents(span, entry);
//TODO look for server-timings? captureTraceParentFromPerformanceEntries(entry)
diff --git a/src/SplunkSpanAttributesProcessor.ts b/src/SplunkSpanAttributesProcessor.ts
index b63dd99b..aacab75b 100644
--- a/src/SplunkSpanAttributesProcessor.ts
+++ b/src/SplunkSpanAttributesProcessor.ts
@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { SpanAttributes } from '@opentelemetry/api';
+import { Attributes } from '@opentelemetry/api';
import { Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';
export class SplunkSpanAttributesProcessor implements SpanProcessor {
- private readonly _globalAttributes: SpanAttributes;
+ private readonly _globalAttributes: Attributes;
- constructor(globalAttributes: SpanAttributes) {
+ constructor(globalAttributes: Attributes) {
this._globalAttributes = globalAttributes ?? {};
}
- setGlobalAttributes(attributes: SpanAttributes): void {
+ setGlobalAttributes(attributes?: Attributes): void {
if (attributes) {
Object.assign(this._globalAttributes, attributes);
} else {
@@ -34,7 +34,7 @@ export class SplunkSpanAttributesProcessor implements SpanProcessor {
}
}
- _experimental_getGlobalAttributes(): SpanAttributes {
+ getGlobalAttributes(): Attributes {
return this._globalAttributes;
}
diff --git a/src/index.ts b/src/index.ts
index c7d885ff..7a51e8c4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -26,7 +26,7 @@ import {
BufferConfig,
} from '@opentelemetry/sdk-trace-base';
import { WebTracerConfig } from '@opentelemetry/sdk-trace-web';
-import { diag, DiagConsoleLogger, DiagLogLevel, SpanAttributes } from '@opentelemetry/api';
+import { Attributes, diag, DiagConsoleLogger, DiagLogLevel, SpanAttributes } from '@opentelemetry/api';
import { SplunkDocumentLoadInstrumentation } from './SplunkDocumentLoadInstrumentation';
import { SplunkXhrPlugin } from './SplunkXhrPlugin';
import { SplunkFetchInstrumentation } from './SplunkFetchInstrumentation';
@@ -118,6 +118,11 @@ export interface SplunkOtelWebConfig {
* */
environment?: string;
+ /**
+ * Sets a value for the 'app.version' attribute
+ */
+ version?: string;
+
/** Allows configuring how telemetry data is sent to the backend */
exporter?: SplunkOtelWebExporterOptions;
@@ -220,17 +225,23 @@ export interface SplunkOtelWebType extends SplunkOtelWebEventTarget {
attributesProcessor?: SplunkSpanAttributesProcessor;
- setGlobalAttributes: (attributes: SpanAttributes) => void;
+ setGlobalAttributes: (attributes: Attributes) => void;
/**
* This method provides access to computed, final value of global attributes, which are applied to all created spans.
- * It is exposed as *experimental*, and could be changed or removed without notice.
*/
- _experimental_getGlobalAttributes: () => SpanAttributes;
+ getGlobalAttributes: () => Attributes;
+ /**
+ * @deprecated Use {@link getGlobalAttributes()}
+ */
+ _experimental_getGlobalAttributes: () => Attributes;
/**
- * This method returns current session ID. It is exposed as *experimental*, and could be changed or removed without
- * notice.
+ * This method returns current session ID
+ */
+ getSessionId: () => SessionIdType;
+ /**
+ * @deprecated Use {@link getSessionId()}
*/
_experimental_getSessionId: () => SessionIdType;
@@ -249,6 +260,7 @@ let inited = false;
let _deregisterInstrumentations: () => void | undefined;
let _deinitSessionTracking: () => void | undefined;
let _errorInstrumentation: SplunkErrorInstrumentation | undefined;
+let _postDocLoadInstrumentation: SplunkPostDocLoadResourceInstrumentation | undefined;
let eventTarget: InternalEventTarget | undefined;
export const SplunkRum: SplunkOtelWebType = {
DEFAULT_AUTO_INSTRUMENTED_EVENTS,
@@ -307,7 +319,7 @@ export const SplunkRum: SplunkOtelWebType = {
processedOptions.cookieDomain,
).deinit;
- const { ignoreUrls, app, environment } = processedOptions;
+ const { ignoreUrls, app, environment, version } = processedOptions;
// enabled: false prevents registerInstrumentations from enabling instrumentations in constructor
// they will be enabled in registerInstrumentations
const pluginDefaults = { ignoreUrls, enabled: false };
@@ -346,6 +358,9 @@ export const SplunkRum: SplunkOtelWebType = {
if (confKey === ERROR_INSTRUMENTATION_NAME && instrumentation instanceof SplunkErrorInstrumentation) {
_errorInstrumentation = instrumentation;
}
+ if (confKey === 'postload' && instrumentation instanceof SplunkPostDocLoadResourceInstrumentation) {
+ _postDocLoadInstrumentation = instrumentation;
+ }
return instrumentation;
}
@@ -354,6 +369,7 @@ export const SplunkRum: SplunkOtelWebType = {
this.attributesProcessor = new SplunkSpanAttributesProcessor({
...environment ? { environment } : {},
+ ...version ? { 'app.version': version } : {},
...processedOptions.globalAttributes || {},
});
provider.addSpanProcessor(this.attributesProcessor);
@@ -380,9 +396,11 @@ export const SplunkRum: SplunkOtelWebType = {
});
provider.register({
- contextManager: new SplunkContextManager(
- processedOptions.context
- )
+ contextManager: new SplunkContextManager({
+ ...processedOptions.context,
+ onBeforeContextStart: () => _postDocLoadInstrumentation.onBeforeContextChange(),
+ onBeforeContextEnd: () => _postDocLoadInstrumentation.onBeforeContextChange(),
+ })
});
// After context manager registration so instrumentation event listeners are affected accordingly
@@ -421,15 +439,19 @@ export const SplunkRum: SplunkOtelWebType = {
inited = false;
},
- setGlobalAttributes(this: SplunkOtelWebType, attributes) {
+ setGlobalAttributes(this: SplunkOtelWebType, attributes?: Attributes) {
this.attributesProcessor?.setGlobalAttributes(attributes);
eventTarget?.emit('global-attributes-changed', {
- attributes: this.attributesProcessor?._experimental_getGlobalAttributes() || {},
+ attributes: this.attributesProcessor?.getGlobalAttributes() || {},
});
},
- _experimental_getGlobalAttributes(this: SplunkOtelWebType) {
- return this.attributesProcessor?._experimental_getGlobalAttributes();
+ getGlobalAttributes(this: SplunkOtelWebType) {
+ return this.attributesProcessor?.getGlobalAttributes() || {};
+ },
+
+ _experimental_getGlobalAttributes() {
+ return this.getGlobalAttributes();
},
error(...args) {
@@ -441,17 +463,28 @@ export const SplunkRum: SplunkOtelWebType = {
_errorInstrumentation.report('SplunkRum.error', args);
},
- _experimental_addEventListener(name, callback): void {
+ addEventListener(name, callback): void {
eventTarget?.addEventListener(name, callback);
},
- _experimental_removeEventListener(name, callback): void {
+ removeEventListener(name, callback): void {
eventTarget?.removeEventListener(name, callback);
},
- _experimental_getSessionId() {
+ _experimental_addEventListener(name, callback): void {
+ return this.addEventListener(name, callback);
+ },
+
+ _experimental_removeEventListener(name, callback): void {
+ return this.removeEventListener(name, callback);
+ },
+
+ getSessionId() {
return getRumSessionId();
},
+ _experimental_getSessionId() {
+ return this.getSessionId();
+ },
};
export default SplunkRum;
diff --git a/src/throwIfUnsupportedBrowser.ts b/src/throwIfUnsupportedBrowser.ts
index 19161774..851b56df 100644
--- a/src/throwIfUnsupportedBrowser.ts
+++ b/src/throwIfUnsupportedBrowser.ts
@@ -32,6 +32,10 @@ if( typeof Symbol !== 'function') {
ParentBasedSampler,
SessionBasedSampler,
_internalInit: noop,
+ addEventListener: noop,
+ getGlobalAttributes: () => ({}),
+ getSessionId: () => undefined,
+ removeEventListener: noop,
_experimental_addEventListener: noop,
_experimental_getGlobalAttributes: () => ({}),
_experimental_getSessionId: () => undefined,
diff --git a/src/version.ts b/src/version.ts
index d7da9425..0f3ad8ed 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -15,4 +15,4 @@ limitations under the License.
*/
// this is an autogenerated file, see scripts/version-update.js
-export const VERSION = '0.11.3-rrweb.7';
+export const VERSION = '0.12.1-rrweb.0';
diff --git a/test/SplunkExporter.test.ts b/test/SplunkExporter.test.ts
index 7a3e1204..34ff5907 100644
--- a/test/SplunkExporter.test.ts
+++ b/test/SplunkExporter.test.ts
@@ -38,7 +38,7 @@ function buildDummySpan({
duration: timeInputToHrTime(1000),
status: { code: api.SpanStatusCode.UNSET },
resource: { attributes: {} },
- events: [],
+ events: [] as ({time: api.HrTime; name: string})[],
};
}
@@ -125,6 +125,39 @@ describe('SplunkExporter', () => {
expect(sentSpan.tags['shortValue']).to.eq('c'.repeat(4000));
});
+ it('filters out missing cors timings', () => {
+ exporter = new SplunkExporter({
+ beaconUrl: 'https://localhost',
+ });
+
+ const dummySpan = buildDummySpan({
+ name: 'asd',
+ attributes: {
+ 'http.url': 'https://example.com/resource.png'
+ },
+ });
+ dummySpan.events.push({
+ time: dummySpan.startTime,
+ name: 'fetchStart'
+ });
+ dummySpan.events.push({
+ time: timeInputToHrTime(0),
+ name: 'connectStart'
+ });
+ dummySpan.events.push({
+ time: timeInputToHrTime(0),
+ name: 'connectEnd'
+ });
+ dummySpan.events.push({
+ time: dummySpan.startTime,
+ name: 'responseEnd'
+ });
+ exporter.export([dummySpan], () => {});
+
+ const sentSpan = JSON.parse(beaconSenderMock.getCall(0).args[1])[0];
+ expect(sentSpan.annotations.length).to.eq(2);
+ });
+
it('allows hooking into serialization', () => {
exporter = new SplunkExporter({
beaconUrl: 'https://localhost',
diff --git a/test/SplunkOtelWeb.test.ts b/test/SplunkOtelWeb.test.ts
index b74b4619..e788f378 100644
--- a/test/SplunkOtelWeb.test.ts
+++ b/test/SplunkOtelWeb.test.ts
@@ -34,7 +34,7 @@ describe('SplunkOtelWeb', () => {
key1: 'value1',
},
});
- expect(SplunkRum._experimental_getGlobalAttributes()).to.deep.eq({
+ expect(SplunkRum.getGlobalAttributes()).to.deep.eq({
key1: 'value1',
});
});
@@ -55,7 +55,7 @@ describe('SplunkOtelWeb', () => {
key3: 'value3',
});
- expect(SplunkRum._experimental_getGlobalAttributes()).to.deep.eq({
+ expect(SplunkRum.getGlobalAttributes()).to.deep.eq({
key1: 'value1',
key2: 'value2-changed',
key3: 'value3',
@@ -74,7 +74,7 @@ describe('SplunkOtelWeb', () => {
});
let receivedAttributes: SpanAttributes | undefined;
- SplunkRum._experimental_addEventListener(
+ SplunkRum.addEventListener(
'global-attributes-changed',
({ payload }) => {
receivedAttributes = payload.attributes;
@@ -99,17 +99,17 @@ describe('SplunkOtelWeb', () => {
describe('session ID', () => {
it('should be readable', () => {
- expect(SplunkRum._experimental_getSessionId()).to.eq(undefined);
+ expect(SplunkRum.getSessionId()).to.eq(undefined);
SplunkRum.init({
app: 'app-name',
beaconUrl: 'https://beacon',
rumAuth: ''
});
- expect(SplunkRum._experimental_getSessionId()).to.match(/[0-9a-f]{32}/);
+ expect(SplunkRum.getSessionId()).to.match(/[0-9a-f]{32}/);
SplunkRum.deinit();
- expect(SplunkRum._experimental_getSessionId()).to.eq(undefined);
+ expect(SplunkRum.getSessionId()).to.eq(undefined);
});
it('should produce notifications when updated', async () => {
@@ -120,7 +120,7 @@ describe('SplunkOtelWeb', () => {
beaconUrl: 'https://beacon',
rumAuth: ''
});
- SplunkRum._experimental_addEventListener(
+ SplunkRum.addEventListener(
'session-changed',
(ev) => { sessionId = ev.payload.sessionId; },
);
diff --git a/test/SplunkSpanAttributesProcessor.test.ts b/test/SplunkSpanAttributesProcessor.test.ts
index 1f2b6975..635df24e 100644
--- a/test/SplunkSpanAttributesProcessor.test.ts
+++ b/test/SplunkSpanAttributesProcessor.test.ts
@@ -24,7 +24,7 @@ describe('SplunkSpanAttributesProcessor', () => {
key1: 'value1',
});
- expect(processor._experimental_getGlobalAttributes()).to.deep.eq({
+ expect(processor.getGlobalAttributes()).to.deep.eq({
key1: 'value1',
});
});
@@ -40,7 +40,7 @@ describe('SplunkSpanAttributesProcessor', () => {
key3: 'value3',
});
- expect(processor._experimental_getGlobalAttributes()).to.deep.eq({
+ expect(processor.getGlobalAttributes()).to.deep.eq({
key1: 'value1',
key2: 'value2-updaged',
key3: 'value3',
diff --git a/test/init.test.ts b/test/init.test.ts
index c3099fee..14b4a0a3 100644
--- a/test/init.test.ts
+++ b/test/init.test.ts
@@ -145,6 +145,7 @@ describe('test init', () => {
SplunkRum.provider.getTracer('test').startSpan('testSpan').end();
setTimeout(() => {
expect(exportMock.called).to.eq(true);
+ SplunkRum.deinit();
done();
});
});
@@ -172,6 +173,7 @@ describe('creating spans is possible', () => {
const exposedSpan = span as tracing.Span;
assert.ok(exposedSpan.attributes['location.href'], 'Checking location.href');
assert.strictEqual(exposedSpan.attributes['environment'], 'my-env');
+ assert.strictEqual(exposedSpan.attributes['app.version'], '1.2-test.3');
assert.strictEqual(exposedSpan.attributes.customerType, 'GOLD');
// Attributes set on resource that zipkin exporter merges to span tags
assert.ok(exposedSpan.resource.attributes['telemetry.sdk.name'], 'Checking telemetry.sdk.name');
diff --git a/test/utils.ts b/test/utils.ts
index 990da2c3..ded5bdc2 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -58,6 +58,7 @@ export function initWithDefaultConfig(capturer: SpanCapturer, additionalOptions
allowInsecureBeacon: true,
app: 'my-app',
environment: 'my-env',
+ version: '1.2-test.3',
globalAttributes: { customerType: 'GOLD' },
bufferTimeout: 0,
rumAuth: undefined,
@@ -78,6 +79,7 @@ export function initWithSyncPipeline(additionalOptions = {}): {
allowInsecureBeacon: true,
app: 'my-app',
environment: 'my-env',
+ version: '1.2-test.3',
bufferTimeout: 0,
rumAuth: undefined,
exporter: { factory: () => exporter },
diff --git a/utils/devServer/assets/test-alt.js b/utils/devServer/assets/test-alt.js
new file mode 100644
index 00000000..ef49c7ee
--- /dev/null
+++ b/utils/devServer/assets/test-alt.js
@@ -0,0 +1,16 @@
+/*
+Copyright 2020 Splunk Inc.
+
+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/utils/devServer/templateProvider.js b/utils/devServer/templateProvider.js
index c4ae2acb..8b032edf 100644
--- a/utils/devServer/templateProvider.js
+++ b/utils/devServer/templateProvider.js
@@ -20,6 +20,7 @@ const fs = require('fs');
const { render } = require('ejs');
const INJECT_TEMPLATE = `
+