@sentry/browser-utils 11.4.0 → 11.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/getNativeImplementation.js +8 -1
- package/build/cjs/getNativeImplementation.js.map +1 -1
- package/build/cjs/performance/entries.js +43 -39
- package/build/cjs/performance/entries.js.map +1 -1
- package/build/cjs/performance/interactions.js +4 -1
- package/build/cjs/performance/interactions.js.map +1 -1
- package/build/cjs/performance/resourceTiming.js +7 -5
- package/build/cjs/performance/resourceTiming.js.map +1 -1
- package/build/cjs/performance/userTiming.js +17 -22
- package/build/cjs/performance/userTiming.js.map +1 -1
- package/build/cjs/web-vitals/emitSpan.js +3 -3
- package/build/cjs/web-vitals/emitSpan.js.map +1 -1
- package/build/cjs/web-vitals/softNavs.js +1 -1
- package/build/cjs/web-vitals/softNavs.js.map +1 -1
- package/build/cjs/web-vitals/spans.js +9 -9
- package/build/cjs/web-vitals/spans.js.map +1 -1
- package/build/esm/getNativeImplementation.js +8 -1
- package/build/esm/getNativeImplementation.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/performance/entries.js +45 -41
- package/build/esm/performance/entries.js.map +1 -1
- package/build/esm/performance/interactions.js +5 -2
- package/build/esm/performance/interactions.js.map +1 -1
- package/build/esm/performance/resourceTiming.js +9 -7
- package/build/esm/performance/resourceTiming.js.map +1 -1
- package/build/esm/performance/userTiming.js +19 -24
- package/build/esm/performance/userTiming.js.map +1 -1
- package/build/esm/web-vitals/emitSpan.js +5 -5
- package/build/esm/web-vitals/emitSpan.js.map +1 -1
- package/build/esm/web-vitals/softNavs.js +3 -3
- package/build/esm/web-vitals/softNavs.js.map +1 -1
- package/build/esm/web-vitals/spans.js +11 -11
- package/build/esm/web-vitals/spans.js.map +1 -1
- package/build/types/getNativeImplementation.d.ts.map +1 -1
- package/build/types/performance/entries.d.ts +3 -3
- package/build/types/performance/entries.d.ts.map +1 -1
- package/build/types/performance/resourceTiming.d.ts.map +1 -1
- package/build/types/performance/userTiming.d.ts +1 -1
- package/build/types/performance/userTiming.d.ts.map +1 -1
- package/build/types/web-vitals/emitSpan.d.ts.map +1 -1
- package/build/types/web-vitals/spans.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -41,7 +41,14 @@ function fetch(...rest) {
|
|
|
41
41
|
return getNativeImplementation("fetch")(...rest);
|
|
42
42
|
}
|
|
43
43
|
function setTimeout(...rest) {
|
|
44
|
-
|
|
44
|
+
try {
|
|
45
|
+
return getNativeImplementation("setTimeout")(...rest);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
debugBuild.DEBUG_BUILD && core.debug.warn("Could not use native setTimeout implementation, falling back to window.setTimeout: ", e);
|
|
48
|
+
const fallback = types.WINDOW.setTimeout.bind(types.WINDOW);
|
|
49
|
+
cachedImplementations.setTimeout = fallback;
|
|
50
|
+
return fallback(...rest);
|
|
51
|
+
}
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
exports.clearCachedImplementation = clearCachedImplementation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getNativeImplementation.js","sources":["../../src/getNativeImplementation.ts"],"sourcesContent":["import { debug, isNativeFunction } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './types';\n\n/**\n * We generally want to use window.fetch / window.setTimeout.\n * However, in some cases this may be wrapped (e.g. by Zone.js for Angular),\n * so we try to get an unpatched version of this from a sandboxed iframe.\n */\n\ninterface CacheableImplementations {\n setTimeout: typeof WINDOW.setTimeout;\n fetch: typeof WINDOW.fetch;\n}\n\nconst cachedImplementations: Partial<CacheableImplementations> = {};\n\n/**\n * Get the native implementation of a browser function.\n *\n * This can be used to ensure we get an unwrapped version of a function, in cases where a wrapped function can lead to problems.\n *\n * The following methods can be retrieved:\n * - `setTimeout`: This can be wrapped by e.g. Angular, causing change detection to be triggered.\n * - `fetch`: This can be wrapped by e.g. ad-blockers, causing an infinite loop when a request is blocked.\n */\nexport function getNativeImplementation<T extends keyof CacheableImplementations>(\n name: T,\n): CacheableImplementations[T] {\n const cached = cachedImplementations[name];\n if (cached) {\n return cached;\n }\n\n let impl = WINDOW[name] as CacheableImplementations[T];\n\n // Fast path to avoid DOM I/O\n if (isNativeFunction(impl)) {\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n }\n\n const document = WINDOW.document;\n // eslint-disable-next-line typescript/no-deprecated\n if (document && typeof document.createElement === 'function') {\n try {\n const sandbox = document.createElement('iframe');\n sandbox.hidden = true;\n document.head.appendChild(sandbox);\n const contentWindow = sandbox.contentWindow;\n if (contentWindow?.[name]) {\n impl = contentWindow[name] as CacheableImplementations[T];\n }\n document.head.removeChild(sandbox);\n } catch (e) {\n // Could not create sandbox iframe, just use window.xxx\n DEBUG_BUILD && debug.warn(`Could not create sandbox iframe for ${name} check, bailing to window.${name}: `, e);\n }\n }\n\n // Sanity check: This _should_ not happen, but if it does, we just skip caching...\n // This can happen e.g. in tests where fetch may not be available in the env, or similar.\n if (!impl) {\n return impl;\n }\n\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n}\n\n/** Clear a cached implementation. */\nexport function clearCachedImplementation(name: keyof CacheableImplementations): void {\n cachedImplementations[name] = undefined;\n}\n\n/**\n * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.\n * Whenever someone wraps the Fetch API and returns the wrong promise chain,\n * this chain becomes orphaned and there is no possible way to capture it's rejections\n * other than allowing it bubble up to this very handler. eg.\n *\n * const f = window.fetch;\n * window.fetch = function () {\n * const p = f.apply(this, arguments);\n *\n * p.then(function() {\n * console.log('hi.');\n * });\n *\n * return p;\n * }\n *\n * `p.then(function () { ... })` is producing a completely separate promise chain,\n * however, what's returned is `p` - the result of original `fetch` call.\n *\n * This mean, that whenever we use the Fetch API to send our own requests, _and_\n * some ad-blocker blocks it, this orphaned chain will _always_ reject,\n * effectively causing another event to be captured.\n * This makes a whole process become an infinite loop, which we need to somehow\n * deal with, and break it in one way or another.\n *\n * To deal with this issue, we are making sure that we _always_ use the real\n * browser Fetch API, instead of relying on what `window.fetch` exposes.\n * The only downside to this would be missing our own requests as breadcrumbs,\n * but because we are already not doing this, it should be just fine.\n *\n * Possible failed fetch error messages per-browser:\n *\n * Chrome: Failed to fetch\n * Edge: Failed to Fetch\n * Firefox: NetworkError when attempting to fetch resource\n * Safari: resource blocked by content blocker\n */\nexport function fetch(...rest: Parameters<typeof WINDOW.fetch>): ReturnType<typeof WINDOW.fetch> {\n return getNativeImplementation('fetch')(...rest);\n}\n\n/**\n * Get an unwrapped `setTimeout` method.\n * This ensures that even if e.g. Angular wraps `setTimeout`, we get the native implementation,\n * avoiding triggering change detection.\n */\nexport function setTimeout(...rest: Parameters<typeof WINDOW.setTimeout>): ReturnType<typeof WINDOW.setTimeout> {\n return getNativeImplementation('setTimeout')(...rest);\n}\n"],"names":["WINDOW","isNativeFunction","DEBUG_BUILD","debug"],"mappings":";;;;;;AAeA,MAAM,wBAA2D,EAAC;AAW3D,SAAS,wBACd,IAAA,EAC6B;AAC7B,EAAA,MAAM,MAAA,GAAS,sBAAsB,IAAI,CAAA;AACzC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAA,GAAOA,aAAO,IAAI,CAAA;AAGtB,EAAA,IAAIC,qBAAA,CAAiB,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAKD,YAAM,CAAA;AAAA,EACxD;AAEA,EAAA,MAAM,WAAWA,YAAA,CAAO,QAAA;AAExB,EAAA,IAAI,QAAA,IAAY,OAAO,QAAA,CAAS,aAAA,KAAkB,UAAA,EAAY;AAC5D,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC/C,MAAA,OAAA,CAAQ,MAAA,GAAS,IAAA;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AACjC,MAAA,MAAM,gBAAgB,OAAA,CAAQ,aAAA;AAC9B,MAAA,IAAI,aAAA,GAAgB,IAAI,CAAA,EAAG;AACzB,QAAA,IAAA,GAAO,cAAc,IAAI,CAAA;AAAA,MAC3B;AACA,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,IACnC,SAAS,CAAA,EAAG;AAEV,MAAAE,sBAAA,IAAeC,WAAM,IAAA,CAAK,CAAA,oCAAA,EAAuC,IAAI,CAAA,0BAAA,EAA6B,IAAI,MAAM,CAAC,CAAA;AAAA,IAC/G;AAAA,EACF;AAIA,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAKH,YAAM,CAAA;AACxD;AAGO,SAAS,0BAA0B,IAAA,EAA4C;AACpF,EAAA,qBAAA,CAAsB,IAAI,CAAA,GAAI,MAAA;AAChC;AAwCO,SAAS,SAAS,IAAA,EAAwE;AAC/F,EAAA,OAAO,uBAAA,CAAwB,OAAO,CAAA,CAAE,GAAG,IAAI,CAAA;AACjD;AAOO,SAAS,cAAc,IAAA,EAAkF;AAC9G,EAAA,OAAO,uBAAA,CAAwB,YAAY,CAAA,CAAE,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"getNativeImplementation.js","sources":["../../src/getNativeImplementation.ts"],"sourcesContent":["import { debug, isNativeFunction } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport { WINDOW } from './types';\n\n/**\n * We generally want to use window.fetch / window.setTimeout.\n * However, in some cases this may be wrapped (e.g. by Zone.js for Angular),\n * so we try to get an unpatched version of this from a sandboxed iframe.\n */\n\ninterface CacheableImplementations {\n setTimeout: typeof WINDOW.setTimeout;\n fetch: typeof WINDOW.fetch;\n}\n\nconst cachedImplementations: Partial<CacheableImplementations> = {};\n\n/**\n * Get the native implementation of a browser function.\n *\n * This can be used to ensure we get an unwrapped version of a function, in cases where a wrapped function can lead to problems.\n *\n * The following methods can be retrieved:\n * - `setTimeout`: This can be wrapped by e.g. Angular, causing change detection to be triggered.\n * - `fetch`: This can be wrapped by e.g. ad-blockers, causing an infinite loop when a request is blocked.\n */\nexport function getNativeImplementation<T extends keyof CacheableImplementations>(\n name: T,\n): CacheableImplementations[T] {\n const cached = cachedImplementations[name];\n if (cached) {\n return cached;\n }\n\n let impl = WINDOW[name] as CacheableImplementations[T];\n\n // Fast path to avoid DOM I/O\n if (isNativeFunction(impl)) {\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n }\n\n const document = WINDOW.document;\n // eslint-disable-next-line typescript/no-deprecated\n if (document && typeof document.createElement === 'function') {\n try {\n const sandbox = document.createElement('iframe');\n sandbox.hidden = true;\n document.head.appendChild(sandbox);\n const contentWindow = sandbox.contentWindow;\n if (contentWindow?.[name]) {\n impl = contentWindow[name] as CacheableImplementations[T];\n }\n document.head.removeChild(sandbox);\n } catch (e) {\n // Could not create sandbox iframe, just use window.xxx\n DEBUG_BUILD && debug.warn(`Could not create sandbox iframe for ${name} check, bailing to window.${name}: `, e);\n }\n }\n\n // Sanity check: This _should_ not happen, but if it does, we just skip caching...\n // This can happen e.g. in tests where fetch may not be available in the env, or similar.\n if (!impl) {\n return impl;\n }\n\n return (cachedImplementations[name] = impl.bind(WINDOW) as CacheableImplementations[T]);\n}\n\n/** Clear a cached implementation. */\nexport function clearCachedImplementation(name: keyof CacheableImplementations): void {\n cachedImplementations[name] = undefined;\n}\n\n/**\n * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.\n * Whenever someone wraps the Fetch API and returns the wrong promise chain,\n * this chain becomes orphaned and there is no possible way to capture it's rejections\n * other than allowing it bubble up to this very handler. eg.\n *\n * const f = window.fetch;\n * window.fetch = function () {\n * const p = f.apply(this, arguments);\n *\n * p.then(function() {\n * console.log('hi.');\n * });\n *\n * return p;\n * }\n *\n * `p.then(function () { ... })` is producing a completely separate promise chain,\n * however, what's returned is `p` - the result of original `fetch` call.\n *\n * This mean, that whenever we use the Fetch API to send our own requests, _and_\n * some ad-blocker blocks it, this orphaned chain will _always_ reject,\n * effectively causing another event to be captured.\n * This makes a whole process become an infinite loop, which we need to somehow\n * deal with, and break it in one way or another.\n *\n * To deal with this issue, we are making sure that we _always_ use the real\n * browser Fetch API, instead of relying on what `window.fetch` exposes.\n * The only downside to this would be missing our own requests as breadcrumbs,\n * but because we are already not doing this, it should be just fine.\n *\n * Possible failed fetch error messages per-browser:\n *\n * Chrome: Failed to fetch\n * Edge: Failed to Fetch\n * Firefox: NetworkError when attempting to fetch resource\n * Safari: resource blocked by content blocker\n */\nexport function fetch(...rest: Parameters<typeof WINDOW.fetch>): ReturnType<typeof WINDOW.fetch> {\n return getNativeImplementation('fetch')(...rest);\n}\n\n/**\n * Get an unwrapped `setTimeout` method.\n * This ensures that even if e.g. Angular wraps `setTimeout`, we get the native implementation,\n * avoiding triggering change detection.\n */\nexport function setTimeout(...rest: Parameters<typeof WINDOW.setTimeout>): ReturnType<typeof WINDOW.setTimeout> {\n try {\n return getNativeImplementation('setTimeout')(...rest);\n } catch (e) {\n // The implementation retrieved from the (since removed) sandbox iframe may throw when invoked,\n // e.g. Firefox throws `NS_ERROR_NOT_INITIALIZED` for timers of a detached window.\n // In this case, fall back to (and cache) the window's own setTimeout.\n DEBUG_BUILD && debug.warn('Could not use native setTimeout implementation, falling back to window.setTimeout: ', e);\n const fallback = WINDOW.setTimeout.bind(WINDOW) as typeof WINDOW.setTimeout;\n cachedImplementations.setTimeout = fallback;\n return fallback(...rest);\n }\n}\n"],"names":["WINDOW","isNativeFunction","DEBUG_BUILD","debug"],"mappings":";;;;;;AAeA,MAAM,wBAA2D,EAAC;AAW3D,SAAS,wBACd,IAAA,EAC6B;AAC7B,EAAA,MAAM,MAAA,GAAS,sBAAsB,IAAI,CAAA;AACzC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,IAAA,GAAOA,aAAO,IAAI,CAAA;AAGtB,EAAA,IAAIC,qBAAA,CAAiB,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAKD,YAAM,CAAA;AAAA,EACxD;AAEA,EAAA,MAAM,WAAWA,YAAA,CAAO,QAAA;AAExB,EAAA,IAAI,QAAA,IAAY,OAAO,QAAA,CAAS,aAAA,KAAkB,UAAA,EAAY;AAC5D,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC/C,MAAA,OAAA,CAAQ,MAAA,GAAS,IAAA;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AACjC,MAAA,MAAM,gBAAgB,OAAA,CAAQ,aAAA;AAC9B,MAAA,IAAI,aAAA,GAAgB,IAAI,CAAA,EAAG;AACzB,QAAA,IAAA,GAAO,cAAc,IAAI,CAAA;AAAA,MAC3B;AACA,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,OAAO,CAAA;AAAA,IACnC,SAAS,CAAA,EAAG;AAEV,MAAAE,sBAAA,IAAeC,WAAM,IAAA,CAAK,CAAA,oCAAA,EAAuC,IAAI,CAAA,0BAAA,EAA6B,IAAI,MAAM,CAAC,CAAA;AAAA,IAC/G;AAAA,EACF;AAIA,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAQ,qBAAA,CAAsB,IAAI,CAAA,GAAI,IAAA,CAAK,KAAKH,YAAM,CAAA;AACxD;AAGO,SAAS,0BAA0B,IAAA,EAA4C;AACpF,EAAA,qBAAA,CAAsB,IAAI,CAAA,GAAI,MAAA;AAChC;AAwCO,SAAS,SAAS,IAAA,EAAwE;AAC/F,EAAA,OAAO,uBAAA,CAAwB,OAAO,CAAA,CAAE,GAAG,IAAI,CAAA;AACjD;AAOO,SAAS,cAAc,IAAA,EAAkF;AAC9G,EAAA,IAAI;AACF,IAAA,OAAO,uBAAA,CAAwB,YAAY,CAAA,CAAE,GAAG,IAAI,CAAA;AAAA,EACtD,SAAS,CAAA,EAAG;AAIV,IAAAE,sBAAA,IAAeC,UAAA,CAAM,IAAA,CAAK,qFAAA,EAAuF,CAAC,CAAA;AAClH,IAAA,MAAM,QAAA,GAAWH,YAAA,CAAO,UAAA,CAAW,IAAA,CAAKA,YAAM,CAAA;AAC9C,IAAA,qBAAA,CAAsB,UAAA,GAAa,QAAA;AACnC,IAAA,OAAO,QAAA,CAAS,GAAG,IAAI,CAAA;AAAA,EACzB;AACF;;;;;;;"}
|
|
@@ -18,7 +18,10 @@ function startTrackingLongTasks() {
|
|
|
18
18
|
}
|
|
19
19
|
const { attributes: parentAttributes, start_timestamp: parentStartTimestamp } = core.spanToJSON(parent);
|
|
20
20
|
for (const entry of entries) {
|
|
21
|
-
const startTime =
|
|
21
|
+
const startTime = core._INTERNAL_performanceTimeToSeconds(entry.startTime);
|
|
22
|
+
if (!startTime) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
22
25
|
const duration = utils.msToSec(entry.duration);
|
|
23
26
|
if (parentAttributes[attributes.SENTRY_OP] === "navigation" && parentStartTimestamp && startTime < parentStartTimestamp) {
|
|
24
27
|
continue;
|
|
@@ -27,7 +30,7 @@ function startTrackingLongTasks() {
|
|
|
27
30
|
name: core.UI_LONG_TASK_SPAN_NAME_FALLBACK,
|
|
28
31
|
op: op.UI_LONG_TASK,
|
|
29
32
|
attributes: {
|
|
30
|
-
[
|
|
33
|
+
[attributes.SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
31
34
|
}
|
|
32
35
|
});
|
|
33
36
|
}
|
|
@@ -40,10 +43,10 @@ function startTrackingLongAnimationFrames() {
|
|
|
40
43
|
return;
|
|
41
44
|
}
|
|
42
45
|
for (const entry of list.getEntries()) {
|
|
43
|
-
|
|
46
|
+
const startTime = core._INTERNAL_performanceTimeToSeconds(entry.startTime);
|
|
47
|
+
if (!startTime || !entry.scripts[0]) {
|
|
44
48
|
continue;
|
|
45
49
|
}
|
|
46
|
-
const startTime = utils.msToSec(core.browserPerformanceTimeOrigin() + entry.startTime);
|
|
47
50
|
const {
|
|
48
51
|
start_timestamp: parentStartTimestamp,
|
|
49
52
|
attributes: { [attributes.SENTRY_OP]: parentOp }
|
|
@@ -53,7 +56,7 @@ function startTrackingLongAnimationFrames() {
|
|
|
53
56
|
}
|
|
54
57
|
const duration = utils.msToSec(entry.duration);
|
|
55
58
|
const attributes$1 = {
|
|
56
|
-
[
|
|
59
|
+
[attributes.SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
57
60
|
};
|
|
58
61
|
const initialScript = entry.scripts[0];
|
|
59
62
|
const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;
|
|
@@ -79,16 +82,17 @@ function startTrackingLongAnimationFrames() {
|
|
|
79
82
|
}
|
|
80
83
|
function addPerformanceEntries(span, options) {
|
|
81
84
|
const performance = utils.getBrowserPerformanceAPI();
|
|
82
|
-
|
|
83
|
-
if (!performance?.getEntries || !origin) {
|
|
85
|
+
if (!performance?.getEntries) {
|
|
84
86
|
return;
|
|
85
87
|
}
|
|
86
88
|
const { spanStreamingEnabled, ignoreResourceSpans } = options;
|
|
87
|
-
const timeOrigin = utils.msToSec(origin);
|
|
88
89
|
const performanceEntries = performance.getEntries();
|
|
89
90
|
const { attributes: attributes$1, start_timestamp: transactionStartTime } = core.spanToJSON(span);
|
|
90
91
|
performanceEntries.slice(_performanceCursor).forEach((entry) => {
|
|
91
|
-
const
|
|
92
|
+
const startTimestamp = core._INTERNAL_performanceTimeToSeconds(entry.startTime);
|
|
93
|
+
if (!startTimestamp) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
92
96
|
const duration = utils.msToSec(
|
|
93
97
|
// Inexplicably, Chrome sometimes emits a negative duration. We need to work around this.
|
|
94
98
|
// There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display
|
|
@@ -96,16 +100,16 @@ function addPerformanceEntries(span, options) {
|
|
|
96
100
|
// TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans.
|
|
97
101
|
Math.max(0, entry.duration)
|
|
98
102
|
);
|
|
99
|
-
if (attributes$1[attributes.SENTRY_OP] === "navigation" && transactionStartTime &&
|
|
103
|
+
if (attributes$1[attributes.SENTRY_OP] === "navigation" && transactionStartTime && startTimestamp < transactionStartTime) {
|
|
100
104
|
return;
|
|
101
105
|
}
|
|
102
106
|
switch (entry.entryType) {
|
|
103
107
|
case "navigation": {
|
|
104
|
-
_addNavigationSpans(span, entry,
|
|
108
|
+
_addNavigationSpans(span, entry, spanStreamingEnabled);
|
|
105
109
|
break;
|
|
106
110
|
}
|
|
107
111
|
case "paint": {
|
|
108
|
-
_addPaintSpan(span, entry,
|
|
112
|
+
_addPaintSpan(span, entry, startTimestamp, duration);
|
|
109
113
|
break;
|
|
110
114
|
}
|
|
111
115
|
case "resource": {
|
|
@@ -113,9 +117,8 @@ function addPerformanceEntries(span, options) {
|
|
|
113
117
|
span,
|
|
114
118
|
entry,
|
|
115
119
|
entry.name,
|
|
116
|
-
|
|
120
|
+
startTimestamp,
|
|
117
121
|
duration,
|
|
118
|
-
timeOrigin,
|
|
119
122
|
ignoreResourceSpans,
|
|
120
123
|
spanStreamingEnabled
|
|
121
124
|
);
|
|
@@ -126,29 +129,31 @@ function addPerformanceEntries(span, options) {
|
|
|
126
129
|
_performanceCursor = Math.max(performanceEntries.length - 1, 0);
|
|
127
130
|
_trackNavigator(span, spanStreamingEnabled);
|
|
128
131
|
}
|
|
129
|
-
function _addPaintSpan(span, entry,
|
|
130
|
-
const startTimestamp = timeOrigin + startTime;
|
|
132
|
+
function _addPaintSpan(span, entry, startTimestamp, duration) {
|
|
131
133
|
utils.startAndEndSpan(span, startTimestamp, startTimestamp + duration, {
|
|
132
134
|
// The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name
|
|
133
135
|
// the conventions ask for, so only the attribute backing it has to be added.
|
|
134
136
|
name: entry.name,
|
|
135
137
|
attributes: {
|
|
136
138
|
[attributes.SENTRY_OP]: op.BROWSER_PAINT,
|
|
137
|
-
[
|
|
139
|
+
[attributes.SENTRY_ORIGIN]: "auto.resource.browser.metrics",
|
|
138
140
|
[attributes.BROWSER_PAINT_TYPE]: entry.name
|
|
139
141
|
}
|
|
140
142
|
});
|
|
141
143
|
}
|
|
142
|
-
function _addNavigationSpans(span, entry,
|
|
143
|
-
_addPerformanceNavigationTiming(span, entry, "unloadEvent",
|
|
144
|
-
_addPerformanceNavigationTiming(span, entry, "redirect",
|
|
145
|
-
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent",
|
|
146
|
-
_addPerformanceNavigationTiming(span, entry, "loadEvent",
|
|
147
|
-
_addPerformanceNavigationTiming(span, entry, "connect",
|
|
148
|
-
_addPerformanceNavigationTiming(span, entry, "secureConnection",
|
|
149
|
-
_addPerformanceNavigationTiming(span, entry, "fetch",
|
|
150
|
-
_addPerformanceNavigationTiming(span, entry, "domainLookup",
|
|
151
|
-
_addRequest(span, entry,
|
|
144
|
+
function _addNavigationSpans(span, entry, spanStreamingEnabled) {
|
|
145
|
+
_addPerformanceNavigationTiming(span, entry, "unloadEvent", spanStreamingEnabled);
|
|
146
|
+
_addPerformanceNavigationTiming(span, entry, "redirect", spanStreamingEnabled);
|
|
147
|
+
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent", spanStreamingEnabled);
|
|
148
|
+
_addPerformanceNavigationTiming(span, entry, "loadEvent", spanStreamingEnabled);
|
|
149
|
+
_addPerformanceNavigationTiming(span, entry, "connect", spanStreamingEnabled);
|
|
150
|
+
_addPerformanceNavigationTiming(span, entry, "secureConnection", spanStreamingEnabled);
|
|
151
|
+
_addPerformanceNavigationTiming(span, entry, "fetch", spanStreamingEnabled);
|
|
152
|
+
_addPerformanceNavigationTiming(span, entry, "domainLookup", spanStreamingEnabled);
|
|
153
|
+
_addRequest(span, entry, spanStreamingEnabled);
|
|
154
|
+
}
|
|
155
|
+
function _navigationTimeToSeconds(entry, time) {
|
|
156
|
+
return core._INTERNAL_performanceTimeToSeconds(time, entry.startTime);
|
|
152
157
|
}
|
|
153
158
|
const NAVIGATION_TIMING_SPAN_OPS = {
|
|
154
159
|
secureConnection: op.BROWSER_TLS_SSL,
|
|
@@ -160,7 +165,7 @@ const NAVIGATION_TIMING_SPAN_OPS = {
|
|
|
160
165
|
domContentLoadedEvent: op.BROWSER_DOM_CONTENT_LOADED_EVENT,
|
|
161
166
|
loadEvent: op.BROWSER_LOAD_EVENT
|
|
162
167
|
};
|
|
163
|
-
function _addPerformanceNavigationTiming(span, entry, event,
|
|
168
|
+
function _addPerformanceNavigationTiming(span, entry, event, spanStreamingEnabled) {
|
|
164
169
|
const eventEnd = _getEndPropertyNameForNavigationTiming(event);
|
|
165
170
|
const end = entry[eventEnd];
|
|
166
171
|
const start = entry[`${event}Start`];
|
|
@@ -168,13 +173,13 @@ function _addPerformanceNavigationTiming(span, entry, event, timeOrigin, spanStr
|
|
|
168
173
|
return;
|
|
169
174
|
}
|
|
170
175
|
const op = NAVIGATION_TIMING_SPAN_OPS[event];
|
|
171
|
-
utils.startAndEndSpan(span,
|
|
176
|
+
utils.startAndEndSpan(span, _navigationTimeToSeconds(entry, start), _navigationTimeToSeconds(entry, end), {
|
|
172
177
|
// With span streaming, span names have to be low cardinality, so we can't fall back to the
|
|
173
178
|
// document URL. `url.full` keeps it, and is what Relay derives the description from.
|
|
174
179
|
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,
|
|
175
180
|
attributes: {
|
|
176
181
|
[attributes.SENTRY_OP]: op,
|
|
177
|
-
[
|
|
182
|
+
[attributes.SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
178
183
|
[attributes.URL_FULL]: core.filterCollectedUrl(entry.name),
|
|
179
184
|
...event === "redirect" && entry.redirectCount != null ? { "http.redirect_count": entry.redirectCount } : {}
|
|
180
185
|
}
|
|
@@ -189,17 +194,17 @@ function _getEndPropertyNameForNavigationTiming(event) {
|
|
|
189
194
|
}
|
|
190
195
|
return `${event}End`;
|
|
191
196
|
}
|
|
192
|
-
function _addRequest(span, entry,
|
|
193
|
-
const requestStartTimestamp =
|
|
194
|
-
const responseEndTimestamp =
|
|
195
|
-
const responseStartTimestamp =
|
|
197
|
+
function _addRequest(span, entry, spanStreamingEnabled) {
|
|
198
|
+
const requestStartTimestamp = _navigationTimeToSeconds(entry, entry.requestStart);
|
|
199
|
+
const responseEndTimestamp = _navigationTimeToSeconds(entry, entry.responseEnd);
|
|
200
|
+
const responseStartTimestamp = _navigationTimeToSeconds(entry, entry.responseStart);
|
|
196
201
|
if (entry.responseEnd) {
|
|
197
202
|
const url = core.filterCollectedUrl(entry.name);
|
|
198
203
|
utils.startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {
|
|
199
204
|
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op.BROWSER_REQUEST] : entry.name,
|
|
200
205
|
attributes: {
|
|
201
206
|
[attributes.SENTRY_OP]: op.BROWSER_REQUEST,
|
|
202
|
-
[
|
|
207
|
+
[attributes.SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
203
208
|
[attributes.URL_FULL]: url
|
|
204
209
|
}
|
|
205
210
|
});
|
|
@@ -207,13 +212,13 @@ function _addRequest(span, entry, timeOrigin, spanStreamingEnabled) {
|
|
|
207
212
|
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op.BROWSER_RESPONSE] : entry.name,
|
|
208
213
|
attributes: {
|
|
209
214
|
[attributes.SENTRY_OP]: op.BROWSER_RESPONSE,
|
|
210
|
-
[
|
|
215
|
+
[attributes.SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
211
216
|
[attributes.URL_FULL]: url
|
|
212
217
|
}
|
|
213
218
|
});
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
|
-
function _addResourceSpans(span, entry, resourceUrl,
|
|
221
|
+
function _addResourceSpans(span, entry, resourceUrl, startTimestamp, duration, ignoredResourceSpanOps, spanStreamingEnabled) {
|
|
217
222
|
if (entry.initiatorType === "xmlhttprequest" || entry.initiatorType === "fetch") {
|
|
218
223
|
return;
|
|
219
224
|
}
|
|
@@ -222,7 +227,7 @@ function _addResourceSpans(span, entry, resourceUrl, startTime, duration, timeOr
|
|
|
222
227
|
return;
|
|
223
228
|
}
|
|
224
229
|
const attributes$1 = {
|
|
225
|
-
[
|
|
230
|
+
[attributes.SENTRY_ORIGIN]: "auto.resource.browser.metrics"
|
|
226
231
|
};
|
|
227
232
|
const parsedUrl = core.parseUrl(resourceUrl);
|
|
228
233
|
if (parsedUrl.protocol) {
|
|
@@ -250,7 +255,6 @@ function _addResourceSpans(span, entry, resourceUrl, startTime, duration, timeOr
|
|
|
250
255
|
["deliveryType", "http.response_delivery_type"]
|
|
251
256
|
]);
|
|
252
257
|
const attributesWithResourceTiming = { ...attributes$1, ...resourceTiming.resourceTimingToSpanAttributes(entry) };
|
|
253
|
-
const startTimestamp = timeOrigin + startTime;
|
|
254
258
|
const endTimestamp = startTimestamp + duration;
|
|
255
259
|
utils.startAndEndSpan(span, startTimestamp, endTimestamp, {
|
|
256
260
|
// With span streaming, span names have to be low cardinality, so we can't fall back to the URL.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.js","sources":["../../../src/performance/entries.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n BROWSER_NAVIGATION_TIMING_SPAN_NAMES,\n browserPerformanceTimeOrigin,\n getActiveSpan,\n parseUrl,\n RESOURCE_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n setMeasurement,\n spanToJSON,\n filterCollectedUrl,\n UI_LONG_TASK_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport {\n BROWSER_PAINT_TYPE,\n CODE_FILE_PATH,\n CODE_FUNCTION_NAME,\n HTTP_REQUEST_SAME_ORIGIN,\n HTTP_RESPONSE_BODY_SIZE,\n HTTP_RESPONSE_SIZE,\n HTTP_RESPONSE_STATUS_CODE,\n NETWORK_CONNECTION_EFFECTIVE_TYPE,\n NETWORK_CONNECTION_RTT,\n NETWORK_CONNECTION_TYPE,\n SENTRY_OP,\n SERVER_ADDRESS,\n URL_DOMAIN,\n URL_FULL,\n URL_SCHEME,\n} from '@sentry/conventions/attributes';\nimport {\n BROWSER_CACHE,\n BROWSER_CONNECT,\n BROWSER_DNS,\n BROWSER_DOM_CONTENT_LOADED_EVENT,\n BROWSER_LOAD_EVENT,\n BROWSER_PAINT,\n BROWSER_REDIRECT,\n BROWSER_REQUEST,\n BROWSER_RESPONSE,\n BROWSER_TLS_SSL,\n BROWSER_UNLOAD_EVENT,\n RESOURCE_OTHER,\n UI_LONG_ANIMATION_FRAME,\n UI_LONG_TASK,\n} from '@sentry/conventions/op';\nimport {\n addPerformanceInstrumentationHandler,\n type PerformanceLongAnimationFrameTiming,\n} from '../instrumentation/performanceObserver';\nimport { WINDOW } from '../types';\nimport { resourceTimingToSpanAttributes } from './resourceTiming';\nimport { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils';\n\ninterface NavigatorNetworkInformation {\n readonly connection?: NetworkInformation;\n}\n\n// http://wicg.github.io/netinfo/#connection-types\ntype ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax';\n\n// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum\ntype EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';\n\n// http://wicg.github.io/netinfo/#dom-megabit\ntype Megabit = number;\n// http://wicg.github.io/netinfo/#dom-millisecond\ntype Millisecond = number;\n\n// http://wicg.github.io/netinfo/#networkinformation-interface\ninterface NetworkInformation extends EventTarget {\n // http://wicg.github.io/netinfo/#type-attribute\n readonly type?: ConnectionType;\n // http://wicg.github.io/netinfo/#effectivetype-attribute\n readonly effectiveType?: EffectiveConnectionType;\n // http://wicg.github.io/netinfo/#downlinkmax-attribute\n readonly downlinkMax?: Megabit;\n // http://wicg.github.io/netinfo/#downlink-attribute\n readonly downlink?: Megabit;\n // http://wicg.github.io/netinfo/#rtt-attribute\n readonly rtt?: Millisecond;\n // http://wicg.github.io/netinfo/#savedata-attribute\n readonly saveData?: boolean;\n // http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection\n onchange?: EventListener;\n}\n\n// https://w3c.github.io/device-memory/#sec-device-memory-js-api\ninterface NavigatorDeviceMemory {\n readonly deviceMemory?: number;\n}\n\nconst MAX_INT_AS_BYTES = 2147483647;\n\nlet _performanceCursor: number = 0;\n\n/**\n * Start tracking long tasks.\n */\nexport function startTrackingLongTasks(): void {\n addPerformanceInstrumentationHandler('longtask', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n\n const { attributes: parentAttributes, start_timestamp: parentStartTimestamp } = spanToJSON(parent);\n\n for (const entry of entries) {\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n const duration = msToSec(entry.duration);\n\n if (parentAttributes[SENTRY_OP] === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding a span if the long task started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_TASK,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n }\n });\n}\n\n/**\n * Start tracking long animation frames.\n */\nexport function startTrackingLongAnimationFrames(): void {\n // NOTE: the current web-vitals version (3.5.2) does not support long-animation-frame, so\n // we directly observe `long-animation-frame` events instead of through the web-vitals\n // `observe` helper function.\n const observer = new PerformanceObserver(list => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of list.getEntries() as PerformanceLongAnimationFrameTiming[]) {\n if (!entry.scripts[0]) {\n continue;\n }\n\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n\n const {\n start_timestamp: parentStartTimestamp,\n attributes: { [SENTRY_OP]: parentOp },\n } = spanToJSON(parent);\n\n if (parentOp === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding the span if the long animation frame started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n const duration = msToSec(entry.duration);\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n };\n\n const initialScript = entry.scripts[0];\n const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;\n attributes['browser.script.invoker'] = invoker;\n attributes['browser.script.invoker_type'] = invokerType;\n if (sourceURL) {\n attributes[CODE_FILE_PATH] = sourceURL;\n }\n if (sourceFunctionName) {\n attributes[CODE_FUNCTION_NAME] = sourceFunctionName;\n }\n if (sourceCharPosition !== -1) {\n attributes['browser.script.source_char_position'] = sourceCharPosition;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_ANIMATION_FRAME,\n attributes,\n });\n }\n });\n\n observer.observe({ type: 'long-animation-frame', buffered: true });\n}\n\ninterface AddPerformanceEntriesOptions {\n /**\n * Resource spans with `op`s matching strings in the array will not be emitted.\n *\n * Default: []\n */\n ignoreResourceSpans: Array<'resource.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/** Add performance related spans to a transaction */\nexport function addPerformanceEntries(span: Span, options: AddPerformanceEntriesOptions): void {\n const performance = getBrowserPerformanceAPI();\n const origin = browserPerformanceTimeOrigin();\n if (!performance?.getEntries || !origin) {\n // Gatekeeper if performance API not available\n return;\n }\n\n const { spanStreamingEnabled, ignoreResourceSpans } = options;\n\n const timeOrigin = msToSec(origin);\n\n const performanceEntries = performance.getEntries();\n\n const { attributes, start_timestamp: transactionStartTime } = spanToJSON(span);\n\n performanceEntries.slice(_performanceCursor).forEach(entry => {\n const startTime = msToSec(entry.startTime);\n const duration = msToSec(\n // Inexplicably, Chrome sometimes emits a negative duration. We need to work around this.\n // There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display\n // The way we clamp the value is probably not accurate, since we have observed this happen for things that may take a while to load, like for example the replay worker.\n // TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans.\n Math.max(0, entry.duration),\n );\n\n if (\n attributes[SENTRY_OP] === 'navigation' &&\n transactionStartTime &&\n timeOrigin + startTime < transactionStartTime\n ) {\n return;\n }\n\n switch (entry.entryType) {\n case 'navigation': {\n _addNavigationSpans(span, entry as PerformanceNavigationTiming, timeOrigin, spanStreamingEnabled);\n break;\n }\n case 'paint': {\n _addPaintSpan(span, entry, startTime, duration, timeOrigin);\n break;\n }\n case 'resource': {\n _addResourceSpans(\n span,\n entry as PerformanceResourceTiming,\n entry.name,\n startTime,\n duration,\n timeOrigin,\n ignoreResourceSpans,\n spanStreamingEnabled,\n );\n break;\n }\n // Ignore other entry types.\n }\n });\n\n _performanceCursor = Math.max(performanceEntries.length - 1, 0);\n\n _trackNavigator(span, spanStreamingEnabled);\n}\n\n/**\n * Create a span for a browser paint performance entry.\n * Exported only for tests.\n */\nexport function _addPaintSpan(\n span: Span,\n entry: PerformanceEntry,\n startTime: number,\n duration: number,\n timeOrigin: number,\n): void {\n const startTimestamp = timeOrigin + startTime;\n\n startAndEndSpan(span, startTimestamp, startTimestamp + duration, {\n // The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name\n // the conventions ask for, so only the attribute backing it has to be added.\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_PAINT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n [BROWSER_PAINT_TYPE]: entry.name,\n },\n });\n}\n\n/**\n * Instrument navigation entries\n * exported only for tests\n */\nexport function _addNavigationSpans(\n span: Span,\n entry: PerformanceNavigationTiming,\n timeOrigin: number,\n spanStreamingEnabled?: boolean,\n): void {\n _addPerformanceNavigationTiming(span, entry, 'unloadEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'redirect', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domContentLoadedEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'loadEvent', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'connect', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'secureConnection', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'fetch', timeOrigin, spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domainLookup', timeOrigin, spanStreamingEnabled);\n\n _addRequest(span, entry, timeOrigin, spanStreamingEnabled);\n}\n\ntype StartEventName =\n | 'secureConnection'\n | 'fetch'\n | 'domainLookup'\n | 'unloadEvent'\n | 'redirect'\n | 'connect'\n | 'domContentLoadedEvent'\n | 'loadEvent';\n\nconst NAVIGATION_TIMING_SPAN_OPS = {\n secureConnection: BROWSER_TLS_SSL,\n fetch: BROWSER_CACHE,\n domainLookup: BROWSER_DNS,\n unloadEvent: BROWSER_UNLOAD_EVENT,\n redirect: BROWSER_REDIRECT,\n connect: BROWSER_CONNECT,\n domContentLoadedEvent: BROWSER_DOM_CONTENT_LOADED_EVENT,\n loadEvent: BROWSER_LOAD_EVENT,\n} as const satisfies Record<StartEventName, string>;\n\ntype EndEventName =\n | 'domainLookupStart'\n | 'domainLookupEnd'\n | 'unloadEventEnd'\n | 'redirectEnd'\n | 'connectEnd'\n | 'domContentLoadedEventEnd'\n | 'loadEventEnd';\n\n/** Create performance navigation related spans */\nfunction _addPerformanceNavigationTiming(\n span: Span,\n entry: PerformanceNavigationTiming,\n event: StartEventName,\n timeOrigin: number,\n spanStreamingEnabled: boolean | undefined,\n): void {\n const eventEnd = _getEndPropertyNameForNavigationTiming(event) satisfies keyof PerformanceNavigationTiming;\n const end = entry[eventEnd];\n const start = entry[`${event}Start`];\n if (!start || !end) {\n return;\n }\n const op = NAVIGATION_TIMING_SPAN_OPS[event];\n startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,\n attributes: {\n [SENTRY_OP]: op,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: filterCollectedUrl(entry.name),\n ...(event === 'redirect' && entry.redirectCount != null ? { 'http.redirect_count': entry.redirectCount } : {}),\n },\n });\n}\n\nfunction _getEndPropertyNameForNavigationTiming(event: StartEventName): EndEventName {\n if (event === 'secureConnection') {\n return 'connectEnd';\n }\n if (event === 'fetch') {\n return 'domainLookupStart';\n }\n return `${event}End`;\n}\n\n/** Create request and response related spans */\nfunction _addRequest(\n span: Span,\n entry: PerformanceNavigationTiming,\n timeOrigin: number,\n spanStreamingEnabled: boolean | undefined,\n): void {\n const requestStartTimestamp = timeOrigin + msToSec(entry.requestStart);\n const responseEndTimestamp = timeOrigin + msToSec(entry.responseEnd);\n const responseStartTimestamp = timeOrigin + msToSec(entry.responseStart);\n if (entry.responseEnd) {\n // It is possible that we are collecting these metrics when the page hasn't finished loading yet, for example when the HTML slowly streams in.\n // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0.\n // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect\n // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans.\n\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n const url = filterCollectedUrl(entry.name);\n\n startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_REQUEST] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_REQUEST,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n\n startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_RESPONSE] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_RESPONSE,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n }\n}\n\n/**\n * Create resource-related spans.\n * Exported only for tests.\n */\nexport function _addResourceSpans(\n span: Span,\n entry: PerformanceResourceTiming,\n resourceUrl: string,\n startTime: number,\n duration: number,\n timeOrigin: number,\n ignoredResourceSpanOps?: Array<string>,\n spanStreamingEnabled?: boolean,\n): void {\n // we already instrument based on fetch and xhr, so we don't need to\n // duplicate spans here.\n if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {\n return;\n }\n\n const op = entry.initiatorType ? `resource.${entry.initiatorType}` : RESOURCE_OTHER;\n if (ignoredResourceSpanOps?.includes(op)) {\n return;\n }\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n };\n\n const parsedUrl = parseUrl(resourceUrl);\n\n if (parsedUrl.protocol) {\n attributes[URL_SCHEME] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.\n }\n\n // `host` is the URL authority, so it can carry userinfo, which doesn't belong on either attribute.\n const host = parsedUrl.host?.replace(/^.*@/, '');\n\n if (host) {\n attributes[SERVER_ADDRESS] = host;\n }\n\n // Unlike `server.address`, `url.domain` excludes the port.\n const domain = host?.replace(/:\\d+$/, '');\n\n if (domain) {\n attributes[URL_DOMAIN] = domain;\n }\n\n attributes[HTTP_REQUEST_SAME_ORIGIN] = resourceUrl.includes(WINDOW.location.origin);\n\n attributes[URL_FULL] = filterCollectedUrl(resourceUrl);\n\n _setResourceRequestAttributes(entry, attributes, [\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n ['responseStatus', HTTP_RESPONSE_STATUS_CODE],\n\n ['transferSize', HTTP_RESPONSE_SIZE],\n ['encodedBodySize', HTTP_RESPONSE_BODY_SIZE],\n ['decodedBodySize', 'http.response.body.decoded_size'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus\n ['renderBlockingStatus', 'resource.render_blocking_status'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType\n ['deliveryType', 'http.response_delivery_type'],\n ]);\n\n const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) };\n\n const startTimestamp = timeOrigin + startTime;\n const endTimestamp = startTimestamp + duration;\n\n startAndEndSpan(span, startTimestamp, endTimestamp, {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n name: spanStreamingEnabled\n ? domain || RESOURCE_SPAN_NAME_FALLBACK\n : resourceUrl.replace(WINDOW.location.origin, ''),\n op,\n attributes: attributesWithResourceTiming,\n });\n}\n\n/**\n * Capture the information of the user agent.\n * TODO(v12): Remove non-span-streaming attributes and measurements once the static trace lifecycle is removed\n */\nfunction _trackNavigator(span: Span, spanStreamingEnabled: boolean | undefined): void {\n const navigator = WINDOW.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory);\n if (!navigator) {\n return;\n }\n\n // track network connectivity\n const connection = navigator.connection;\n if (connection) {\n if (connection.effectiveType) {\n span.setAttribute(\n spanStreamingEnabled ? NETWORK_CONNECTION_EFFECTIVE_TYPE : 'effectiveConnectionType',\n connection.effectiveType,\n );\n }\n\n if (connection.type) {\n span.setAttribute(spanStreamingEnabled ? NETWORK_CONNECTION_TYPE : 'connectionType', connection.type);\n }\n\n if (isMeasurementValue(connection.rtt)) {\n if (spanStreamingEnabled) {\n span.setAttribute(NETWORK_CONNECTION_RTT, connection.rtt);\n } else if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n // Measurements are only recorded on the pageload span, matching the historical\n // behavior where `connection.rtt` was only flushed for pageload transactions.\n setMeasurement('connection.rtt', connection.rtt, 'millisecond');\n }\n }\n }\n\n if (isMeasurementValue(navigator.deviceMemory)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.memory.estimated_capacity', navigator.deviceMemory);\n } else {\n span.setAttribute('deviceMemory', `${navigator.deviceMemory} GB`);\n }\n }\n\n if (isMeasurementValue(navigator.hardwareConcurrency)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.processor_count', navigator.hardwareConcurrency);\n } else {\n span.setAttribute('hardwareConcurrency', String(navigator.hardwareConcurrency));\n }\n }\n}\n\ntype ExperimentalResourceTimingProperty =\n | 'renderBlockingStatus'\n | 'deliveryType'\n // For some reason, TS during build, errors on `responseStatus` not being a property of\n // PerformanceResourceTiming while it actually is. Hence, we're adding it here.\n // Perhaps because response status is not yet available in Webkit/Safari.\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n | 'responseStatus';\n\n/**\n * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry.\n *\n * This is just a mapping function for entry->attribute to keep bundle-size minimal.\n * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}).\n * Assumes that all entry properties might be undefined for browser-specific differences.\n * Only accepts string and number values for now and also sets 0-values.\n */\nexport function _setResourceRequestAttributes(\n entry: Partial<PerformanceResourceTiming> & Partial<Record<ExperimentalResourceTimingProperty, number | string>>,\n attributes: SpanAttributes,\n properties: [keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty, string][],\n): void {\n properties.forEach(([entryKey, attributeKey]) => {\n const entryVal = entry[entryKey];\n if (\n entryVal != null &&\n ((typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES) || typeof entryVal === 'string')\n ) {\n attributes[attributeKey] = entryVal;\n }\n });\n}\n"],"names":["addPerformanceInstrumentationHandler","getActiveSpan","spanToJSON","msToSec","browserPerformanceTimeOrigin","SENTRY_OP","startAndEndSpan","UI_LONG_TASK_SPAN_NAME_FALLBACK","UI_LONG_TASK","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","attributes","CODE_FILE_PATH","CODE_FUNCTION_NAME","UI_LONG_ANIMATION_FRAME","getBrowserPerformanceAPI","BROWSER_PAINT","BROWSER_PAINT_TYPE","BROWSER_TLS_SSL","BROWSER_CACHE","BROWSER_DNS","BROWSER_UNLOAD_EVENT","BROWSER_REDIRECT","BROWSER_CONNECT","BROWSER_DOM_CONTENT_LOADED_EVENT","BROWSER_LOAD_EVENT","BROWSER_NAVIGATION_TIMING_SPAN_NAMES","URL_FULL","filterCollectedUrl","BROWSER_REQUEST","BROWSER_RESPONSE","op","RESOURCE_OTHER","parseUrl","URL_SCHEME","SERVER_ADDRESS","URL_DOMAIN","HTTP_REQUEST_SAME_ORIGIN","WINDOW","HTTP_RESPONSE_STATUS_CODE","HTTP_RESPONSE_SIZE","HTTP_RESPONSE_BODY_SIZE","resourceTimingToSpanAttributes","RESOURCE_SPAN_NAME_FALLBACK","NETWORK_CONNECTION_EFFECTIVE_TYPE","NETWORK_CONNECTION_TYPE","isMeasurementValue","NETWORK_CONNECTION_RTT","setMeasurement"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entries.js","sources":["../../../src/performance/entries.ts"],"sourcesContent":["/* eslint-disable max-lines */\nimport type { Span, SpanAttributes } from '@sentry/core';\nimport {\n BROWSER_NAVIGATION_TIMING_SPAN_NAMES,\n getActiveSpan,\n parseUrl,\n _INTERNAL_performanceTimeToSeconds,\n RESOURCE_SPAN_NAME_FALLBACK,\n setMeasurement,\n spanToJSON,\n filterCollectedUrl,\n UI_LONG_TASK_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport {\n BROWSER_PAINT_TYPE,\n CODE_FILE_PATH,\n CODE_FUNCTION_NAME,\n HTTP_REQUEST_SAME_ORIGIN,\n HTTP_RESPONSE_BODY_SIZE,\n HTTP_RESPONSE_SIZE,\n HTTP_RESPONSE_STATUS_CODE,\n NETWORK_CONNECTION_EFFECTIVE_TYPE,\n NETWORK_CONNECTION_RTT,\n NETWORK_CONNECTION_TYPE,\n SENTRY_OP,\n SERVER_ADDRESS,\n URL_DOMAIN,\n URL_FULL,\n URL_SCHEME,\n SENTRY_ORIGIN,\n} from '@sentry/conventions/attributes';\nimport {\n BROWSER_CACHE,\n BROWSER_CONNECT,\n BROWSER_DNS,\n BROWSER_DOM_CONTENT_LOADED_EVENT,\n BROWSER_LOAD_EVENT,\n BROWSER_PAINT,\n BROWSER_REDIRECT,\n BROWSER_REQUEST,\n BROWSER_RESPONSE,\n BROWSER_TLS_SSL,\n BROWSER_UNLOAD_EVENT,\n RESOURCE_OTHER,\n UI_LONG_ANIMATION_FRAME,\n UI_LONG_TASK,\n} from '@sentry/conventions/op';\nimport {\n addPerformanceInstrumentationHandler,\n type PerformanceLongAnimationFrameTiming,\n} from '../instrumentation/performanceObserver';\nimport { WINDOW } from '../types';\nimport { resourceTimingToSpanAttributes } from './resourceTiming';\nimport { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils';\n\ninterface NavigatorNetworkInformation {\n readonly connection?: NetworkInformation;\n}\n\n// http://wicg.github.io/netinfo/#connection-types\ntype ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax';\n\n// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum\ntype EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';\n\n// http://wicg.github.io/netinfo/#dom-megabit\ntype Megabit = number;\n// http://wicg.github.io/netinfo/#dom-millisecond\ntype Millisecond = number;\n\n// http://wicg.github.io/netinfo/#networkinformation-interface\ninterface NetworkInformation extends EventTarget {\n // http://wicg.github.io/netinfo/#type-attribute\n readonly type?: ConnectionType;\n // http://wicg.github.io/netinfo/#effectivetype-attribute\n readonly effectiveType?: EffectiveConnectionType;\n // http://wicg.github.io/netinfo/#downlinkmax-attribute\n readonly downlinkMax?: Megabit;\n // http://wicg.github.io/netinfo/#downlink-attribute\n readonly downlink?: Megabit;\n // http://wicg.github.io/netinfo/#rtt-attribute\n readonly rtt?: Millisecond;\n // http://wicg.github.io/netinfo/#savedata-attribute\n readonly saveData?: boolean;\n // http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection\n onchange?: EventListener;\n}\n\n// https://w3c.github.io/device-memory/#sec-device-memory-js-api\ninterface NavigatorDeviceMemory {\n readonly deviceMemory?: number;\n}\n\nconst MAX_INT_AS_BYTES = 2147483647;\n\nlet _performanceCursor: number = 0;\n\n/**\n * Start tracking long tasks.\n */\nexport function startTrackingLongTasks(): void {\n addPerformanceInstrumentationHandler('longtask', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n\n const { attributes: parentAttributes, start_timestamp: parentStartTimestamp } = spanToJSON(parent);\n\n for (const entry of entries) {\n const startTime = _INTERNAL_performanceTimeToSeconds(entry.startTime);\n if (!startTime) {\n continue;\n }\n const duration = msToSec(entry.duration);\n\n if (parentAttributes[SENTRY_OP] === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding a span if the long task started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_TASK,\n attributes: {\n [SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n }\n });\n}\n\n/**\n * Start tracking long animation frames.\n */\nexport function startTrackingLongAnimationFrames(): void {\n // NOTE: the current web-vitals version (3.5.2) does not support long-animation-frame, so\n // we directly observe `long-animation-frame` events instead of through the web-vitals\n // `observe` helper function.\n const observer = new PerformanceObserver(list => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of list.getEntries() as PerformanceLongAnimationFrameTiming[]) {\n const startTime = _INTERNAL_performanceTimeToSeconds(entry.startTime);\n if (!startTime || !entry.scripts[0]) {\n continue;\n }\n\n const {\n start_timestamp: parentStartTimestamp,\n attributes: { [SENTRY_OP]: parentOp },\n } = spanToJSON(parent);\n\n if (parentOp === 'navigation' && parentStartTimestamp && startTime < parentStartTimestamp) {\n // Skip adding the span if the long animation frame started before the navigation started.\n // `startAndEndSpan` will otherwise adjust the parent's start time to the span's start\n // time, potentially skewing the duration of the actual navigation as reported via our\n // routing instrumentations\n continue;\n }\n const duration = msToSec(entry.duration);\n\n const attributes: SpanAttributes = {\n [SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n };\n\n const initialScript = entry.scripts[0];\n const { invoker, invokerType, sourceURL, sourceFunctionName, sourceCharPosition } = initialScript;\n attributes['browser.script.invoker'] = invoker;\n attributes['browser.script.invoker_type'] = invokerType;\n if (sourceURL) {\n attributes[CODE_FILE_PATH] = sourceURL;\n }\n if (sourceFunctionName) {\n attributes[CODE_FUNCTION_NAME] = sourceFunctionName;\n }\n if (sourceCharPosition !== -1) {\n attributes['browser.script.source_char_position'] = sourceCharPosition;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, {\n name: UI_LONG_TASK_SPAN_NAME_FALLBACK,\n op: UI_LONG_ANIMATION_FRAME,\n attributes,\n });\n }\n });\n\n observer.observe({ type: 'long-animation-frame', buffered: true });\n}\n\ninterface AddPerformanceEntriesOptions {\n /**\n * Resource spans with `op`s matching strings in the array will not be emitted.\n *\n * Default: []\n */\n ignoreResourceSpans: Array<'resource.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;\n\n /**\n * Whether span streaming is enabled.\n */\n spanStreamingEnabled?: boolean;\n}\n\n/** Add performance related spans to a transaction */\nexport function addPerformanceEntries(span: Span, options: AddPerformanceEntriesOptions): void {\n const performance = getBrowserPerformanceAPI();\n if (!performance?.getEntries) {\n // Gatekeeper if performance API not available\n return;\n }\n\n const { spanStreamingEnabled, ignoreResourceSpans } = options;\n\n const performanceEntries = performance.getEntries();\n\n const { attributes, start_timestamp: transactionStartTime } = spanToJSON(span);\n\n performanceEntries.slice(_performanceCursor).forEach(entry => {\n const startTimestamp = _INTERNAL_performanceTimeToSeconds(entry.startTime);\n if (!startTimestamp) {\n return;\n }\n const duration = msToSec(\n // Inexplicably, Chrome sometimes emits a negative duration. We need to work around this.\n // There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display\n // The way we clamp the value is probably not accurate, since we have observed this happen for things that may take a while to load, like for example the replay worker.\n // TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans.\n Math.max(0, entry.duration),\n );\n\n if (attributes[SENTRY_OP] === 'navigation' && transactionStartTime && startTimestamp < transactionStartTime) {\n return;\n }\n\n switch (entry.entryType) {\n case 'navigation': {\n _addNavigationSpans(span, entry as PerformanceNavigationTiming, spanStreamingEnabled);\n break;\n }\n case 'paint': {\n _addPaintSpan(span, entry, startTimestamp, duration);\n break;\n }\n case 'resource': {\n _addResourceSpans(\n span,\n entry as PerformanceResourceTiming,\n entry.name,\n startTimestamp,\n duration,\n ignoreResourceSpans,\n spanStreamingEnabled,\n );\n break;\n }\n // Ignore other entry types.\n }\n });\n\n _performanceCursor = Math.max(performanceEntries.length - 1, 0);\n\n _trackNavigator(span, spanStreamingEnabled);\n}\n\n/**\n * Create a span for a browser paint performance entry.\n * Exported only for tests.\n */\nexport function _addPaintSpan(span: Span, entry: PerformanceEntry, startTimestamp: number, duration: number): void {\n startAndEndSpan(span, startTimestamp, startTimestamp + duration, {\n // The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name\n // the conventions ask for, so only the attribute backing it has to be added.\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_PAINT,\n [SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n [BROWSER_PAINT_TYPE]: entry.name,\n },\n });\n}\n\n/**\n * Instrument navigation entries\n * exported only for tests\n */\nexport function _addNavigationSpans(\n span: Span,\n entry: PerformanceNavigationTiming,\n spanStreamingEnabled?: boolean,\n): void {\n _addPerformanceNavigationTiming(span, entry, 'unloadEvent', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'redirect', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domContentLoadedEvent', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'loadEvent', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'connect', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'secureConnection', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'fetch', spanStreamingEnabled);\n _addPerformanceNavigationTiming(span, entry, 'domainLookup', spanStreamingEnabled);\n\n _addRequest(span, entry, spanStreamingEnabled);\n}\n\n/**\n * Navigations can happen long after page load, after the time origin was corrected for drift. We use the origin from\n * the entry's start for all its timings, so its duration stays correct.\n */\nfunction _navigationTimeToSeconds(entry: PerformanceNavigationTiming, time: number): number {\n // The cast is safe: `addPerformanceEntries` only adds navigation spans if the time origin is available.\n return _INTERNAL_performanceTimeToSeconds(time, entry.startTime) as number;\n}\n\ntype StartEventName =\n | 'secureConnection'\n | 'fetch'\n | 'domainLookup'\n | 'unloadEvent'\n | 'redirect'\n | 'connect'\n | 'domContentLoadedEvent'\n | 'loadEvent';\n\nconst NAVIGATION_TIMING_SPAN_OPS = {\n secureConnection: BROWSER_TLS_SSL,\n fetch: BROWSER_CACHE,\n domainLookup: BROWSER_DNS,\n unloadEvent: BROWSER_UNLOAD_EVENT,\n redirect: BROWSER_REDIRECT,\n connect: BROWSER_CONNECT,\n domContentLoadedEvent: BROWSER_DOM_CONTENT_LOADED_EVENT,\n loadEvent: BROWSER_LOAD_EVENT,\n} as const satisfies Record<StartEventName, string>;\n\ntype EndEventName =\n | 'domainLookupStart'\n | 'domainLookupEnd'\n | 'unloadEventEnd'\n | 'redirectEnd'\n | 'connectEnd'\n | 'domContentLoadedEventEnd'\n | 'loadEventEnd';\n\n/** Create performance navigation related spans */\nfunction _addPerformanceNavigationTiming(\n span: Span,\n entry: PerformanceNavigationTiming,\n event: StartEventName,\n spanStreamingEnabled: boolean | undefined,\n): void {\n const eventEnd = _getEndPropertyNameForNavigationTiming(event) satisfies keyof PerformanceNavigationTiming;\n const end = entry[eventEnd];\n const start = entry[`${event}Start`];\n if (!start || !end) {\n return;\n }\n const op = NAVIGATION_TIMING_SPAN_OPS[event];\n startAndEndSpan(span, _navigationTimeToSeconds(entry, start), _navigationTimeToSeconds(entry, end), {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,\n attributes: {\n [SENTRY_OP]: op,\n [SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: filterCollectedUrl(entry.name),\n ...(event === 'redirect' && entry.redirectCount != null ? { 'http.redirect_count': entry.redirectCount } : {}),\n },\n });\n}\n\nfunction _getEndPropertyNameForNavigationTiming(event: StartEventName): EndEventName {\n if (event === 'secureConnection') {\n return 'connectEnd';\n }\n if (event === 'fetch') {\n return 'domainLookupStart';\n }\n return `${event}End`;\n}\n\n/** Create request and response related spans */\nfunction _addRequest(span: Span, entry: PerformanceNavigationTiming, spanStreamingEnabled: boolean | undefined): void {\n const requestStartTimestamp = _navigationTimeToSeconds(entry, entry.requestStart);\n const responseEndTimestamp = _navigationTimeToSeconds(entry, entry.responseEnd);\n const responseStartTimestamp = _navigationTimeToSeconds(entry, entry.responseStart);\n if (entry.responseEnd) {\n // It is possible that we are collecting these metrics when the page hasn't finished loading yet, for example when the HTML slowly streams in.\n // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0.\n // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect\n // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans.\n\n // With span streaming, span names have to be low cardinality, so we can't fall back to the\n // document URL. `url.full` keeps it, and is what Relay derives the description from.\n const url = filterCollectedUrl(entry.name);\n\n startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_REQUEST] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_REQUEST,\n [SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n\n startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {\n name: spanStreamingEnabled ? BROWSER_NAVIGATION_TIMING_SPAN_NAMES[BROWSER_RESPONSE] : entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_RESPONSE,\n [SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n [URL_FULL]: url,\n },\n });\n }\n}\n\n/**\n * Create resource-related spans.\n * Exported only for tests.\n */\nexport function _addResourceSpans(\n span: Span,\n entry: PerformanceResourceTiming,\n resourceUrl: string,\n startTimestamp: number,\n duration: number,\n ignoredResourceSpanOps?: Array<string>,\n spanStreamingEnabled?: boolean,\n): void {\n // we already instrument based on fetch and xhr, so we don't need to\n // duplicate spans here.\n if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {\n return;\n }\n\n const op = entry.initiatorType ? `resource.${entry.initiatorType}` : RESOURCE_OTHER;\n if (ignoredResourceSpanOps?.includes(op)) {\n return;\n }\n\n const attributes: SpanAttributes = {\n [SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n };\n\n const parsedUrl = parseUrl(resourceUrl);\n\n if (parsedUrl.protocol) {\n attributes[URL_SCHEME] = parsedUrl.protocol.split(':').pop(); // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.\n }\n\n // `host` is the URL authority, so it can carry userinfo, which doesn't belong on either attribute.\n const host = parsedUrl.host?.replace(/^.*@/, '');\n\n if (host) {\n attributes[SERVER_ADDRESS] = host;\n }\n\n // Unlike `server.address`, `url.domain` excludes the port.\n const domain = host?.replace(/:\\d+$/, '');\n\n if (domain) {\n attributes[URL_DOMAIN] = domain;\n }\n\n attributes[HTTP_REQUEST_SAME_ORIGIN] = resourceUrl.includes(WINDOW.location.origin);\n\n attributes[URL_FULL] = filterCollectedUrl(resourceUrl);\n\n _setResourceRequestAttributes(entry, attributes, [\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n ['responseStatus', HTTP_RESPONSE_STATUS_CODE],\n\n ['transferSize', HTTP_RESPONSE_SIZE],\n ['encodedBodySize', HTTP_RESPONSE_BODY_SIZE],\n ['decodedBodySize', 'http.response.body.decoded_size'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus\n ['renderBlockingStatus', 'resource.render_blocking_status'],\n\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType\n ['deliveryType', 'http.response_delivery_type'],\n ]);\n\n const attributesWithResourceTiming: SpanAttributes = { ...attributes, ...resourceTimingToSpanAttributes(entry) };\n\n const endTimestamp = startTimestamp + duration;\n\n startAndEndSpan(span, startTimestamp, endTimestamp, {\n // With span streaming, span names have to be low cardinality, so we can't fall back to the URL.\n name: spanStreamingEnabled\n ? domain || RESOURCE_SPAN_NAME_FALLBACK\n : resourceUrl.replace(WINDOW.location.origin, ''),\n op,\n attributes: attributesWithResourceTiming,\n });\n}\n\n/**\n * Capture the information of the user agent.\n * TODO(v12): Remove non-span-streaming attributes and measurements once the static trace lifecycle is removed\n */\nfunction _trackNavigator(span: Span, spanStreamingEnabled: boolean | undefined): void {\n const navigator = WINDOW.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory);\n if (!navigator) {\n return;\n }\n\n // track network connectivity\n const connection = navigator.connection;\n if (connection) {\n if (connection.effectiveType) {\n span.setAttribute(\n spanStreamingEnabled ? NETWORK_CONNECTION_EFFECTIVE_TYPE : 'effectiveConnectionType',\n connection.effectiveType,\n );\n }\n\n if (connection.type) {\n span.setAttribute(spanStreamingEnabled ? NETWORK_CONNECTION_TYPE : 'connectionType', connection.type);\n }\n\n if (isMeasurementValue(connection.rtt)) {\n if (spanStreamingEnabled) {\n span.setAttribute(NETWORK_CONNECTION_RTT, connection.rtt);\n } else if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {\n // Measurements are only recorded on the pageload span, matching the historical\n // behavior where `connection.rtt` was only flushed for pageload transactions.\n setMeasurement('connection.rtt', connection.rtt, 'millisecond');\n }\n }\n }\n\n if (isMeasurementValue(navigator.deviceMemory)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.memory.estimated_capacity', navigator.deviceMemory);\n } else {\n span.setAttribute('deviceMemory', `${navigator.deviceMemory} GB`);\n }\n }\n\n if (isMeasurementValue(navigator.hardwareConcurrency)) {\n if (spanStreamingEnabled) {\n span.setAttribute('device.processor_count', navigator.hardwareConcurrency);\n } else {\n span.setAttribute('hardwareConcurrency', String(navigator.hardwareConcurrency));\n }\n }\n}\n\ntype ExperimentalResourceTimingProperty =\n | 'renderBlockingStatus'\n | 'deliveryType'\n // For some reason, TS during build, errors on `responseStatus` not being a property of\n // PerformanceResourceTiming while it actually is. Hence, we're adding it here.\n // Perhaps because response status is not yet available in Webkit/Safari.\n // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus\n | 'responseStatus';\n\n/**\n * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry.\n *\n * This is just a mapping function for entry->attribute to keep bundle-size minimal.\n * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}).\n * Assumes that all entry properties might be undefined for browser-specific differences.\n * Only accepts string and number values for now and also sets 0-values.\n */\nexport function _setResourceRequestAttributes(\n entry: Partial<PerformanceResourceTiming> & Partial<Record<ExperimentalResourceTimingProperty, number | string>>,\n attributes: SpanAttributes,\n properties: [keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty, string][],\n): void {\n properties.forEach(([entryKey, attributeKey]) => {\n const entryVal = entry[entryKey];\n if (\n entryVal != null &&\n ((typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES) || typeof entryVal === 'string')\n ) {\n attributes[attributeKey] = entryVal;\n }\n });\n}\n"],"names":["addPerformanceInstrumentationHandler","getActiveSpan","spanToJSON","_INTERNAL_performanceTimeToSeconds","msToSec","SENTRY_OP","startAndEndSpan","UI_LONG_TASK_SPAN_NAME_FALLBACK","UI_LONG_TASK","SENTRY_ORIGIN","attributes","CODE_FILE_PATH","CODE_FUNCTION_NAME","UI_LONG_ANIMATION_FRAME","getBrowserPerformanceAPI","BROWSER_PAINT","BROWSER_PAINT_TYPE","BROWSER_TLS_SSL","BROWSER_CACHE","BROWSER_DNS","BROWSER_UNLOAD_EVENT","BROWSER_REDIRECT","BROWSER_CONNECT","BROWSER_DOM_CONTENT_LOADED_EVENT","BROWSER_LOAD_EVENT","BROWSER_NAVIGATION_TIMING_SPAN_NAMES","URL_FULL","filterCollectedUrl","BROWSER_REQUEST","BROWSER_RESPONSE","op","RESOURCE_OTHER","parseUrl","URL_SCHEME","SERVER_ADDRESS","URL_DOMAIN","HTTP_REQUEST_SAME_ORIGIN","WINDOW","HTTP_RESPONSE_STATUS_CODE","HTTP_RESPONSE_SIZE","HTTP_RESPONSE_BODY_SIZE","resourceTimingToSpanAttributes","RESOURCE_SPAN_NAME_FALLBACK","NETWORK_CONNECTION_EFFECTIVE_TYPE","NETWORK_CONNECTION_TYPE","isMeasurementValue","NETWORK_CONNECTION_RTT","setMeasurement"],"mappings":"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"}
|
|
@@ -123,7 +123,10 @@ function trackInteractionsAsSpans(client) {
|
|
|
123
123
|
}
|
|
124
124
|
for (const entry of entries) {
|
|
125
125
|
if (entry.name === "click") {
|
|
126
|
-
const startTime =
|
|
126
|
+
const startTime = core._INTERNAL_performanceTimeToSeconds(entry.startTime);
|
|
127
|
+
if (!startTime) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
127
130
|
const duration = utils.msToSec(entry.duration);
|
|
128
131
|
const selector = htmlTreeAsString.htmlTreeAsString(entry.target);
|
|
129
132
|
const componentName$1 = componentName.getComponentName(entry.target);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions.js","sources":["../../../src/performance/interactions.ts"],"sourcesContent":["import {\n BROWSER_WEB_VITAL_INP_TARGET,\n HTTP_ROUTE,\n ROUTER_NAVIGATION_ROUTE_ID,\n SENTRY_IDLE_SPAN_FINISH_REASON,\n SENTRY_OP,\n SENTRY_ORIGIN,\n SENTRY_SEGMENT_NAME,\n SENTRY_SEGMENT_NAME_SOURCE,\n SENTRY_SOURCE,\n UI_COMPONENT_NAME,\n URL_FULL,\n URL_PATH,\n URL_TEMPLATE,\n} from '@sentry/conventions/attributes';\nimport { UI_ACTION_CLICK, UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport type { Client, IntegrationFn, Span, StartSpanOptions, TransactionSource } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n defineIntegration,\n filterCollectedUrl,\n getActiveSpan,\n getRootSpan,\n hasSpanStreamingEnabled,\n spanToJSON,\n UI_ACTION_CLICK_SPAN_NAME_FALLBACK,\n UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport { startIdleSpan } from '@sentry/core/browser';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport { addPerformanceInstrumentationHandler } from '../instrumentation/performanceObserver';\nimport { UNKNOWN_ELEMENT_NAME } from '../web-vitals/inp';\nimport { isBotUserAgent } from '../isBotUserAgent';\nimport { WINDOW } from '../types';\nimport { msToSec, startAndEndSpan } from './utils';\nimport { getComponentName } from '../component-name';\n\nconst INTEGRATION_NAME = 'Interactions';\n\ninterface InteractionsOptions {\n /**\n * The time that has to pass without any span being created.\n * If this time is exceeded, the interaction span will finish.\n *\n * Default: 1000 (ms)\n */\n idleTimeout?: number;\n\n /**\n * The max. time an interaction span may run.\n * If this time is exceeded, the interaction span will finish no matter what.\n *\n * Default: 30000 (ms)\n */\n finalTimeout?: number;\n\n /**\n * The max. time a child span of an interaction span may run.\n * If this time is exceeded, the interaction span will finish no matter what.\n *\n * Default: 15000 (ms)\n */\n childSpanTimeout?: number;\n}\n\ninterface RouteInfo {\n name: string | undefined;\n source: TransactionSource | undefined;\n routeId: string | undefined;\n urlTemplate: string | undefined;\n httpRoute: string | undefined;\n urlPath: string | undefined;\n urlFull: string | undefined;\n}\n\nconst _interactionsIntegration = ((options: InteractionsOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n if (isBotUserAgent()) {\n return;\n }\n\n const latestRoute: RouteInfo = {\n name: undefined,\n source: undefined,\n routeId: undefined,\n urlTemplate: undefined,\n httpRoute: undefined,\n urlPath: undefined,\n urlFull: undefined,\n };\n // The pageload/navigation span that is currently in progress, if any. Clicks that happen while one\n // is open are not turned into interaction spans, as they'd compete with the route span for children.\n let inflightRouteSpan: Span | undefined;\n\n function trackRoute(span: Span): void {\n const { name, attributes } = spanToJSON(span);\n latestRoute.name = name;\n // oxlint-disable-next-line typescript/no-deprecated\n latestRoute.source = (attributes[SENTRY_SOURCE] || attributes[SENTRY_SEGMENT_NAME_SOURCE]) as\n | TransactionSource\n | undefined;\n latestRoute.routeId = attributes[ROUTER_NAVIGATION_ROUTE_ID] as string | undefined;\n latestRoute.urlTemplate = attributes[URL_TEMPLATE] as string | undefined;\n latestRoute.httpRoute = attributes[HTTP_ROUTE] as string | undefined;\n latestRoute.urlPath = attributes[URL_PATH] as string | undefined;\n latestRoute.urlFull = attributes[URL_FULL] as string | undefined;\n }\n\n client.on('spanStart', span => {\n if (isRouteSpan(span)) {\n inflightRouteSpan = span;\n trackRoute(span);\n }\n });\n\n client.on('spanEnd', span => {\n if (span !== inflightRouteSpan) {\n return;\n }\n inflightRouteSpan = undefined;\n // Re-read the route on end: routing instrumentation frequently renames the pageload or navigation\n // span once the route is resolved, so this is more accurate than what we saw at span start.\n trackRoute(span);\n });\n\n registerInteractionListener(client, latestRoute, () => inflightRouteSpan, options);\n\n trackInteractionsAsSpans(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Captures user interactions as spans.\n *\n * Important: This integration is considered experimental and might lead to noisy spans. Use at your own risk.\n *\n *\n * Clicks that happen outside of an in-progress pageload or navigation start an idle `ui.action.click` span,\n * named after the last known route, which collects everything the interaction triggers (requests, child spans,\n * …). Additionally, `ui.interaction.click` spans are recorded for the browser's own `event` timing entries.\n *\n * The integration requires `browserTracingIntegration` (or another source of pageload/navigation spans)\n * to know which route an interaction belongs to.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [Sentry.browserTracingIntegration(), Sentry.interactionsIntegration()],\n * });\n * ```\n */\nexport const interactionsIntegration = defineIntegration(_interactionsIntegration);\n\nfunction isRouteSpan(span: Span): boolean {\n if (getRootSpan(span) !== span) {\n return false;\n }\n const op = spanToJSON(span).attributes[SENTRY_OP];\n return op === 'pageload' || op === 'navigation';\n}\n\nfunction registerInteractionListener(\n client: Client,\n latestRoute: RouteInfo,\n getInflightRouteSpan: () => Span | undefined,\n // `startIdleSpan` fills in `TRACING_DEFAULTS` for whatever is left out, which are the same defaults\n // `browserTracingIntegration` uses for its pageload and navigation spans.\n idleSpanOptions: InteractionsOptions,\n): void {\n // `document` is not available in all browser environments (e.g. web workers), and without it there is\n // nothing to click on.\n if (!WINDOW.document) {\n return;\n }\n\n let inflightInteractionSpan: Span | undefined;\n\n addEventListener(\n 'click',\n () => {\n if (getInflightRouteSpan()) {\n DEBUG_BUILD &&\n debug.warn(\n `[Tracing] Did not create ${UI_ACTION_CLICK} span because a pageload or navigation span is in progress.`,\n );\n return;\n }\n\n if (inflightInteractionSpan) {\n inflightInteractionSpan.setAttribute(SENTRY_IDLE_SPAN_FINISH_REASON, 'interactionInterrupted');\n inflightInteractionSpan.end();\n inflightInteractionSpan = undefined;\n }\n\n if (!latestRoute.name) {\n DEBUG_BUILD &&\n debug.warn(`[Tracing] Did not create ${UI_ACTION_CLICK} span because the latest route name is missing.`);\n return;\n }\n\n const hasSpanStreaming = hasSpanStreamingEnabled(client);\n const routeName = latestRoute.routeId || latestRoute.urlTemplate || latestRoute.httpRoute;\n const streamedName = routeName || UI_ACTION_CLICK_SPAN_NAME_FALLBACK;\n // A route id is a name the app gave the route, so it is `custom` rather than a `route` pattern.\n const streamedNameSource = !routeName || latestRoute.routeId ? 'custom' : 'route';\n\n inflightInteractionSpan = startIdleSpan(\n {\n name: hasSpanStreaming ? streamedName : latestRoute.name,\n attributes: {\n [SENTRY_OP]: UI_ACTION_CLICK,\n [SENTRY_SEGMENT_NAME_SOURCE]: hasSpanStreaming ? streamedNameSource : latestRoute.source || 'url',\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\n ...(hasSpanStreaming && { [SENTRY_SEGMENT_NAME]: streamedName }),\n ...(latestRoute.routeId && { [ROUTER_NAVIGATION_ROUTE_ID]: latestRoute.routeId }),\n ...(latestRoute.urlTemplate && { [URL_TEMPLATE]: latestRoute.urlTemplate }),\n ...(latestRoute.httpRoute && { [HTTP_ROUTE]: latestRoute.httpRoute }),\n ...(latestRoute.urlPath && { [URL_PATH]: latestRoute.urlPath }),\n ...(latestRoute.urlFull && { [URL_FULL]: filterCollectedUrl(latestRoute.urlFull) }),\n },\n },\n idleSpanOptions,\n );\n },\n { capture: true },\n );\n}\n\n/**\n * Record the browser's `event` timing entries for clicks as spans on the currently active span.\n */\nfunction trackInteractionsAsSpans(client: Client): void {\n addPerformanceInstrumentationHandler('event', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of entries) {\n if (entry.name === 'click') {\n const startTime = msToSec((browserPerformanceTimeOrigin() as number) + entry.startTime);\n const duration = msToSec(entry.duration);\n\n const selector = htmlTreeAsString(entry.target);\n const componentName = getComponentName(entry.target);\n const hasSpanStreaming = hasSpanStreamingEnabled(client);\n\n const spanOptions: StartSpanOptions & Required<Pick<StartSpanOptions, 'attributes'>> = {\n name: hasSpanStreaming ? componentName || UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK : selector,\n startTime: startTime,\n attributes: {\n [SENTRY_OP]: UI_INTERACTION_CLICK,\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\n ...(selector && selector !== UNKNOWN_ELEMENT_NAME && { [BROWSER_WEB_VITAL_INP_TARGET]: selector }),\n },\n };\n\n if (componentName) {\n spanOptions.attributes[UI_COMPONENT_NAME] = componentName;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, spanOptions);\n }\n }\n });\n}\n"],"names":["isBotUserAgent","attributes","spanToJSON","SENTRY_SOURCE","SENTRY_SEGMENT_NAME_SOURCE","ROUTER_NAVIGATION_ROUTE_ID","URL_TEMPLATE","HTTP_ROUTE","URL_PATH","URL_FULL","defineIntegration","getRootSpan","SENTRY_OP","WINDOW","DEBUG_BUILD","debug","UI_ACTION_CLICK","SENTRY_IDLE_SPAN_FINISH_REASON","hasSpanStreamingEnabled","UI_ACTION_CLICK_SPAN_NAME_FALLBACK","startIdleSpan","SENTRY_ORIGIN","SENTRY_SEGMENT_NAME","filterCollectedUrl","addPerformanceInstrumentationHandler","getActiveSpan","msToSec","browserPerformanceTimeOrigin","htmlTreeAsString","componentName","getComponentName","UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK","UI_INTERACTION_CLICK","UNKNOWN_ELEMENT_NAME","BROWSER_WEB_VITAL_INP_TARGET","UI_COMPONENT_NAME","startAndEndSpan"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interactions.js","sources":["../../../src/performance/interactions.ts"],"sourcesContent":["import {\n BROWSER_WEB_VITAL_INP_TARGET,\n HTTP_ROUTE,\n ROUTER_NAVIGATION_ROUTE_ID,\n SENTRY_IDLE_SPAN_FINISH_REASON,\n SENTRY_OP,\n SENTRY_ORIGIN,\n SENTRY_SEGMENT_NAME,\n SENTRY_SEGMENT_NAME_SOURCE,\n SENTRY_SOURCE,\n UI_COMPONENT_NAME,\n URL_FULL,\n URL_PATH,\n URL_TEMPLATE,\n} from '@sentry/conventions/attributes';\nimport { UI_ACTION_CLICK, UI_INTERACTION_CLICK } from '@sentry/conventions/op';\nimport type { Client, IntegrationFn, Span, StartSpanOptions, TransactionSource } from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n filterCollectedUrl,\n getActiveSpan,\n getRootSpan,\n hasSpanStreamingEnabled,\n _INTERNAL_performanceTimeToSeconds,\n spanToJSON,\n UI_ACTION_CLICK_SPAN_NAME_FALLBACK,\n UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK,\n} from '@sentry/core';\nimport { startIdleSpan } from '@sentry/core/browser';\nimport { DEBUG_BUILD } from '../debug-build';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport { addPerformanceInstrumentationHandler } from '../instrumentation/performanceObserver';\nimport { UNKNOWN_ELEMENT_NAME } from '../web-vitals/inp';\nimport { isBotUserAgent } from '../isBotUserAgent';\nimport { WINDOW } from '../types';\nimport { msToSec, startAndEndSpan } from './utils';\nimport { getComponentName } from '../component-name';\n\nconst INTEGRATION_NAME = 'Interactions';\n\ninterface InteractionsOptions {\n /**\n * The time that has to pass without any span being created.\n * If this time is exceeded, the interaction span will finish.\n *\n * Default: 1000 (ms)\n */\n idleTimeout?: number;\n\n /**\n * The max. time an interaction span may run.\n * If this time is exceeded, the interaction span will finish no matter what.\n *\n * Default: 30000 (ms)\n */\n finalTimeout?: number;\n\n /**\n * The max. time a child span of an interaction span may run.\n * If this time is exceeded, the interaction span will finish no matter what.\n *\n * Default: 15000 (ms)\n */\n childSpanTimeout?: number;\n}\n\ninterface RouteInfo {\n name: string | undefined;\n source: TransactionSource | undefined;\n routeId: string | undefined;\n urlTemplate: string | undefined;\n httpRoute: string | undefined;\n urlPath: string | undefined;\n urlFull: string | undefined;\n}\n\nconst _interactionsIntegration = ((options: InteractionsOptions = {}) => {\n return {\n name: INTEGRATION_NAME,\n setup(client) {\n if (isBotUserAgent()) {\n return;\n }\n\n const latestRoute: RouteInfo = {\n name: undefined,\n source: undefined,\n routeId: undefined,\n urlTemplate: undefined,\n httpRoute: undefined,\n urlPath: undefined,\n urlFull: undefined,\n };\n // The pageload/navigation span that is currently in progress, if any. Clicks that happen while one\n // is open are not turned into interaction spans, as they'd compete with the route span for children.\n let inflightRouteSpan: Span | undefined;\n\n function trackRoute(span: Span): void {\n const { name, attributes } = spanToJSON(span);\n latestRoute.name = name;\n // oxlint-disable-next-line typescript/no-deprecated\n latestRoute.source = (attributes[SENTRY_SOURCE] || attributes[SENTRY_SEGMENT_NAME_SOURCE]) as\n | TransactionSource\n | undefined;\n latestRoute.routeId = attributes[ROUTER_NAVIGATION_ROUTE_ID] as string | undefined;\n latestRoute.urlTemplate = attributes[URL_TEMPLATE] as string | undefined;\n latestRoute.httpRoute = attributes[HTTP_ROUTE] as string | undefined;\n latestRoute.urlPath = attributes[URL_PATH] as string | undefined;\n latestRoute.urlFull = attributes[URL_FULL] as string | undefined;\n }\n\n client.on('spanStart', span => {\n if (isRouteSpan(span)) {\n inflightRouteSpan = span;\n trackRoute(span);\n }\n });\n\n client.on('spanEnd', span => {\n if (span !== inflightRouteSpan) {\n return;\n }\n inflightRouteSpan = undefined;\n // Re-read the route on end: routing instrumentation frequently renames the pageload or navigation\n // span once the route is resolved, so this is more accurate than what we saw at span start.\n trackRoute(span);\n });\n\n registerInteractionListener(client, latestRoute, () => inflightRouteSpan, options);\n\n trackInteractionsAsSpans(client);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Captures user interactions as spans.\n *\n * Important: This integration is considered experimental and might lead to noisy spans. Use at your own risk.\n *\n *\n * Clicks that happen outside of an in-progress pageload or navigation start an idle `ui.action.click` span,\n * named after the last known route, which collects everything the interaction triggers (requests, child spans,\n * …). Additionally, `ui.interaction.click` spans are recorded for the browser's own `event` timing entries.\n *\n * The integration requires `browserTracingIntegration` (or another source of pageload/navigation spans)\n * to know which route an interaction belongs to.\n *\n * @example\n * ```ts\n * Sentry.init({\n * integrations: [Sentry.browserTracingIntegration(), Sentry.interactionsIntegration()],\n * });\n * ```\n */\nexport const interactionsIntegration = defineIntegration(_interactionsIntegration);\n\nfunction isRouteSpan(span: Span): boolean {\n if (getRootSpan(span) !== span) {\n return false;\n }\n const op = spanToJSON(span).attributes[SENTRY_OP];\n return op === 'pageload' || op === 'navigation';\n}\n\nfunction registerInteractionListener(\n client: Client,\n latestRoute: RouteInfo,\n getInflightRouteSpan: () => Span | undefined,\n // `startIdleSpan` fills in `TRACING_DEFAULTS` for whatever is left out, which are the same defaults\n // `browserTracingIntegration` uses for its pageload and navigation spans.\n idleSpanOptions: InteractionsOptions,\n): void {\n // `document` is not available in all browser environments (e.g. web workers), and without it there is\n // nothing to click on.\n if (!WINDOW.document) {\n return;\n }\n\n let inflightInteractionSpan: Span | undefined;\n\n addEventListener(\n 'click',\n () => {\n if (getInflightRouteSpan()) {\n DEBUG_BUILD &&\n debug.warn(\n `[Tracing] Did not create ${UI_ACTION_CLICK} span because a pageload or navigation span is in progress.`,\n );\n return;\n }\n\n if (inflightInteractionSpan) {\n inflightInteractionSpan.setAttribute(SENTRY_IDLE_SPAN_FINISH_REASON, 'interactionInterrupted');\n inflightInteractionSpan.end();\n inflightInteractionSpan = undefined;\n }\n\n if (!latestRoute.name) {\n DEBUG_BUILD &&\n debug.warn(`[Tracing] Did not create ${UI_ACTION_CLICK} span because the latest route name is missing.`);\n return;\n }\n\n const hasSpanStreaming = hasSpanStreamingEnabled(client);\n const routeName = latestRoute.routeId || latestRoute.urlTemplate || latestRoute.httpRoute;\n const streamedName = routeName || UI_ACTION_CLICK_SPAN_NAME_FALLBACK;\n // A route id is a name the app gave the route, so it is `custom` rather than a `route` pattern.\n const streamedNameSource = !routeName || latestRoute.routeId ? 'custom' : 'route';\n\n inflightInteractionSpan = startIdleSpan(\n {\n name: hasSpanStreaming ? streamedName : latestRoute.name,\n attributes: {\n [SENTRY_OP]: UI_ACTION_CLICK,\n [SENTRY_SEGMENT_NAME_SOURCE]: hasSpanStreaming ? streamedNameSource : latestRoute.source || 'url',\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\n ...(hasSpanStreaming && { [SENTRY_SEGMENT_NAME]: streamedName }),\n ...(latestRoute.routeId && { [ROUTER_NAVIGATION_ROUTE_ID]: latestRoute.routeId }),\n ...(latestRoute.urlTemplate && { [URL_TEMPLATE]: latestRoute.urlTemplate }),\n ...(latestRoute.httpRoute && { [HTTP_ROUTE]: latestRoute.httpRoute }),\n ...(latestRoute.urlPath && { [URL_PATH]: latestRoute.urlPath }),\n ...(latestRoute.urlFull && { [URL_FULL]: filterCollectedUrl(latestRoute.urlFull) }),\n },\n },\n idleSpanOptions,\n );\n },\n { capture: true },\n );\n}\n\n/**\n * Record the browser's `event` timing entries for clicks as spans on the currently active span.\n */\nfunction trackInteractionsAsSpans(client: Client): void {\n addPerformanceInstrumentationHandler('event', ({ entries }) => {\n const parent = getActiveSpan();\n if (!parent) {\n return;\n }\n for (const entry of entries) {\n if (entry.name === 'click') {\n const startTime = _INTERNAL_performanceTimeToSeconds(entry.startTime);\n if (!startTime) {\n continue;\n }\n const duration = msToSec(entry.duration);\n\n const selector = htmlTreeAsString(entry.target);\n const componentName = getComponentName(entry.target);\n const hasSpanStreaming = hasSpanStreamingEnabled(client);\n\n const spanOptions: StartSpanOptions & Required<Pick<StartSpanOptions, 'attributes'>> = {\n name: hasSpanStreaming ? componentName || UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK : selector,\n startTime: startTime,\n attributes: {\n [SENTRY_OP]: UI_INTERACTION_CLICK,\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\n ...(selector && selector !== UNKNOWN_ELEMENT_NAME && { [BROWSER_WEB_VITAL_INP_TARGET]: selector }),\n },\n };\n\n if (componentName) {\n spanOptions.attributes[UI_COMPONENT_NAME] = componentName;\n }\n\n startAndEndSpan(parent, startTime, startTime + duration, spanOptions);\n }\n }\n });\n}\n"],"names":["isBotUserAgent","attributes","spanToJSON","SENTRY_SOURCE","SENTRY_SEGMENT_NAME_SOURCE","ROUTER_NAVIGATION_ROUTE_ID","URL_TEMPLATE","HTTP_ROUTE","URL_PATH","URL_FULL","defineIntegration","getRootSpan","SENTRY_OP","WINDOW","DEBUG_BUILD","debug","UI_ACTION_CLICK","SENTRY_IDLE_SPAN_FINISH_REASON","hasSpanStreamingEnabled","UI_ACTION_CLICK_SPAN_NAME_FALLBACK","startIdleSpan","SENTRY_ORIGIN","SENTRY_SEGMENT_NAME","filterCollectedUrl","addPerformanceInstrumentationHandler","getActiveSpan","_INTERNAL_performanceTimeToSeconds","msToSec","htmlTreeAsString","componentName","getComponentName","UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK","UI_INTERACTION_CLICK","UNKNOWN_ELEMENT_NAME","BROWSER_WEB_VITAL_INP_TARGET","UI_COMPONENT_NAME","startAndEndSpan"],"mappings":"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"}
|
|
@@ -3,9 +3,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
4
|
const utils = require('./utils.js');
|
|
5
5
|
|
|
6
|
-
function getAbsoluteTime(time) {
|
|
7
|
-
return time ? ((core.browserPerformanceTimeOrigin() || performance.timeOrigin) + time) / 1e3 : time;
|
|
8
|
-
}
|
|
9
6
|
function resourceTimingToSpanAttributes(resourceTiming) {
|
|
10
7
|
const timingSpanData = {};
|
|
11
8
|
if (resourceTiming.nextHopProtocol != void 0) {
|
|
@@ -13,9 +10,14 @@ function resourceTimingToSpanAttributes(resourceTiming) {
|
|
|
13
10
|
timingSpanData["network.protocol.version"] = version;
|
|
14
11
|
timingSpanData["network.protocol.name"] = name;
|
|
15
12
|
}
|
|
16
|
-
if (
|
|
13
|
+
if (core._INTERNAL_performanceTimeToSeconds(resourceTiming.startTime) === void 0) {
|
|
17
14
|
return timingSpanData;
|
|
18
15
|
}
|
|
16
|
+
const getAbsoluteTime = (time) => (
|
|
17
|
+
// falsy values should be preserved so that we can later on drop undefined values and
|
|
18
|
+
// preserve 0 vals for cross-origin resources without proper `Timing-Allow-Origin` header.
|
|
19
|
+
time ? core._INTERNAL_performanceTimeToSeconds(time, resourceTiming.startTime) : time
|
|
20
|
+
);
|
|
19
21
|
return dropUndefinedKeysFromObject({
|
|
20
22
|
...timingSpanData,
|
|
21
23
|
"http.request.redirect_start": getAbsoluteTime(resourceTiming.redirectStart),
|
|
@@ -33,7 +35,7 @@ function resourceTimingToSpanAttributes(resourceTiming) {
|
|
|
33
35
|
// For TTFB we actually want the relative time from timeOrigin to responseStart
|
|
34
36
|
// This way, TTFB always measures the "first page load" experience.
|
|
35
37
|
// see: https://web.dev/articles/ttfb#measure-resource-requests
|
|
36
|
-
"http.request.time_to_first_byte": resourceTiming.responseStart != null ? resourceTiming.responseStart
|
|
38
|
+
"http.request.time_to_first_byte": resourceTiming.responseStart != null ? utils.msToSec(resourceTiming.responseStart) : void 0
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
function dropUndefinedKeysFromObject(attrs) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resourceTiming.js","sources":["../../../src/performance/resourceTiming.ts"],"sourcesContent":["import type { SpanAttributes } from '@sentry/core';\nimport {
|
|
1
|
+
{"version":3,"file":"resourceTiming.js","sources":["../../../src/performance/resourceTiming.ts"],"sourcesContent":["import type { SpanAttributes } from '@sentry/core';\nimport { _INTERNAL_performanceTimeToSeconds } from '@sentry/core';\nimport { extractNetworkProtocol, msToSec } from './utils';\n\n/**\n * Converts a PerformanceResourceTiming entry to span data for the resource span. Most importantly,\n * it converts the timing values from timestamps relative to the `performance.timeOrigin` to absolute timestamps\n * in seconds.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming#timestamps\n *\n * @param resourceTiming\n * @returns An array where the first element is the attribute name and the second element is the attribute value.\n */\nexport function resourceTimingToSpanAttributes(resourceTiming: PerformanceResourceTiming): SpanAttributes {\n const timingSpanData: SpanAttributes = {};\n // Checking for only `undefined` and `null` is intentional because it's\n // valid for `nextHopProtocol` to be an empty string.\n if (resourceTiming.nextHopProtocol != undefined) {\n const { name, version } = extractNetworkProtocol(resourceTiming.nextHopProtocol);\n timingSpanData['network.protocol.version'] = version;\n timingSpanData['network.protocol.name'] = name;\n }\n\n if (_INTERNAL_performanceTimeToSeconds(resourceTiming.startTime) === undefined) {\n return timingSpanData;\n }\n\n // Use the origin from the request start for all timings, so the durations between them stay correct.\n const getAbsoluteTime = (time: number | undefined): number | undefined =>\n // falsy values should be preserved so that we can later on drop undefined values and\n // preserve 0 vals for cross-origin resources without proper `Timing-Allow-Origin` header.\n time ? _INTERNAL_performanceTimeToSeconds(time, resourceTiming.startTime) : time;\n\n return dropUndefinedKeysFromObject({\n ...timingSpanData,\n\n 'http.request.redirect_start': getAbsoluteTime(resourceTiming.redirectStart),\n 'http.request.redirect_end': getAbsoluteTime(resourceTiming.redirectEnd),\n\n 'http.request.worker_start': getAbsoluteTime(resourceTiming.workerStart),\n\n 'http.request.fetch_start': getAbsoluteTime(resourceTiming.fetchStart),\n\n 'http.request.domain_lookup_start': getAbsoluteTime(resourceTiming.domainLookupStart),\n 'http.request.domain_lookup_end': getAbsoluteTime(resourceTiming.domainLookupEnd),\n\n 'http.request.connect_start': getAbsoluteTime(resourceTiming.connectStart),\n 'http.request.secure_connection_start': getAbsoluteTime(resourceTiming.secureConnectionStart),\n 'http.request.connection_end': getAbsoluteTime(resourceTiming.connectEnd),\n\n 'http.request.request_start': getAbsoluteTime(resourceTiming.requestStart),\n\n 'http.request.response_start': getAbsoluteTime(resourceTiming.responseStart),\n 'http.request.response_end': getAbsoluteTime(resourceTiming.responseEnd),\n\n // For TTFB we actually want the relative time from timeOrigin to responseStart\n // This way, TTFB always measures the \"first page load\" experience.\n // see: https://web.dev/articles/ttfb#measure-resource-requests\n 'http.request.time_to_first_byte':\n resourceTiming.responseStart != null ? msToSec(resourceTiming.responseStart) : undefined,\n });\n}\n\n/**\n * Remove properties with `undefined` as value from an object.\n * In contrast to `dropUndefinedKeys` in core this funciton only works on first-level\n * key-value objects and does not recursively go into object properties or arrays.\n */\nfunction dropUndefinedKeysFromObject<T extends object>(attrs: T): Partial<T> {\n return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value != null)) as Partial<T>;\n}\n"],"names":["extractNetworkProtocol","_INTERNAL_performanceTimeToSeconds","msToSec"],"mappings":";;;;;AAcO,SAAS,+BAA+B,cAAA,EAA2D;AACxG,EAAA,MAAM,iBAAiC,EAAC;AAGxC,EAAA,IAAI,cAAA,CAAe,mBAAmB,MAAA,EAAW;AAC/C,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAIA,4BAAA,CAAuB,eAAe,eAAe,CAAA;AAC/E,IAAA,cAAA,CAAe,0BAA0B,CAAA,GAAI,OAAA;AAC7C,IAAA,cAAA,CAAe,uBAAuB,CAAA,GAAI,IAAA;AAAA,EAC5C;AAEA,EAAA,IAAIC,uCAAA,CAAmC,cAAA,CAAe,SAAS,CAAA,KAAM,MAAA,EAAW;AAC9E,IAAA,OAAO,cAAA;AAAA,EACT;AAGA,EAAA,MAAM,kBAAkB,CAAC,IAAA;AAAA;AAAA;AAAA,IAGvB,IAAA,GAAOA,uCAAA,CAAmC,IAAA,EAAM,cAAA,CAAe,SAAS,CAAA,GAAI;AAAA,GAAA;AAE9E,EAAA,OAAO,2BAAA,CAA4B;AAAA,IACjC,GAAG,cAAA;AAAA,IAEH,6BAAA,EAA+B,eAAA,CAAgB,cAAA,CAAe,aAAa,CAAA;AAAA,IAC3E,2BAAA,EAA6B,eAAA,CAAgB,cAAA,CAAe,WAAW,CAAA;AAAA,IAEvE,2BAAA,EAA6B,eAAA,CAAgB,cAAA,CAAe,WAAW,CAAA;AAAA,IAEvE,0BAAA,EAA4B,eAAA,CAAgB,cAAA,CAAe,UAAU,CAAA;AAAA,IAErE,kCAAA,EAAoC,eAAA,CAAgB,cAAA,CAAe,iBAAiB,CAAA;AAAA,IACpF,gCAAA,EAAkC,eAAA,CAAgB,cAAA,CAAe,eAAe,CAAA;AAAA,IAEhF,4BAAA,EAA8B,eAAA,CAAgB,cAAA,CAAe,YAAY,CAAA;AAAA,IACzE,sCAAA,EAAwC,eAAA,CAAgB,cAAA,CAAe,qBAAqB,CAAA;AAAA,IAC5F,6BAAA,EAA+B,eAAA,CAAgB,cAAA,CAAe,UAAU,CAAA;AAAA,IAExE,4BAAA,EAA8B,eAAA,CAAgB,cAAA,CAAe,YAAY,CAAA;AAAA,IAEzE,6BAAA,EAA+B,eAAA,CAAgB,cAAA,CAAe,aAAa,CAAA;AAAA,IAC3E,2BAAA,EAA6B,eAAA,CAAgB,cAAA,CAAe,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA,IAKvE,mCACE,cAAA,CAAe,aAAA,IAAiB,OAAOC,aAAA,CAAQ,cAAA,CAAe,aAAa,CAAA,GAAI;AAAA,GAClF,CAAA;AACH;AAOA,SAAS,4BAA8C,KAAA,EAAsB;AAC3E,EAAA,OAAO,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,KAAK,CAAA,KAAM,KAAA,IAAS,IAAI,CAAC,CAAA;AACtF;;;;"}
|