@sentry/browser-utils 11.0.0-rc.1 → 11.1.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/README.md +7 -4
- package/build/cjs/performance/entries.js +33 -22
- package/build/cjs/performance/entries.js.map +1 -1
- package/build/cjs/performance/interactions.js +38 -11
- package/build/cjs/performance/interactions.js.map +1 -1
- package/build/cjs/web-vitals/emitSpan.js +0 -4
- package/build/cjs/web-vitals/emitSpan.js.map +1 -1
- package/build/cjs/web-vitals/inp.js +1 -0
- package/build/cjs/web-vitals/inp.js.map +1 -1
- package/build/cjs/web-vitals/reportEvents.js +4 -4
- package/build/cjs/web-vitals/reportEvents.js.map +1 -1
- package/build/cjs/web-vitals/softNavs.js +25 -5
- package/build/cjs/web-vitals/softNavs.js.map +1 -1
- package/build/cjs/web-vitals/spans.js +60 -28
- package/build/cjs/web-vitals/spans.js.map +1 -1
- package/build/cjs/web-vitals/tracking.js +7 -0
- package/build/cjs/web-vitals/tracking.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/performance/entries.js +35 -25
- package/build/esm/performance/entries.js.map +1 -1
- package/build/esm/performance/interactions.js +40 -13
- package/build/esm/performance/interactions.js.map +1 -1
- package/build/esm/web-vitals/emitSpan.js +0 -4
- package/build/esm/web-vitals/emitSpan.js.map +1 -1
- package/build/esm/web-vitals/inp.js +1 -1
- package/build/esm/web-vitals/inp.js.map +1 -1
- package/build/esm/web-vitals/reportEvents.js +4 -4
- package/build/esm/web-vitals/reportEvents.js.map +1 -1
- package/build/esm/web-vitals/softNavs.js +25 -5
- package/build/esm/web-vitals/softNavs.js.map +1 -1
- package/build/esm/web-vitals/spans.js +53 -21
- package/build/esm/web-vitals/spans.js.map +1 -1
- package/build/esm/web-vitals/tracking.js +8 -1
- package/build/esm/web-vitals/tracking.js.map +1 -1
- package/build/types/performance/entries.d.ts +6 -1
- package/build/types/performance/entries.d.ts.map +1 -1
- package/build/types/performance/interactions.d.ts.map +1 -1
- package/build/types/web-vitals/emitSpan.d.ts +0 -2
- package/build/types/web-vitals/emitSpan.d.ts.map +1 -1
- package/build/types/web-vitals/inp.d.ts +2 -0
- package/build/types/web-vitals/inp.d.ts.map +1 -1
- package/build/types/web-vitals/reportEvents.d.ts +4 -7
- package/build/types/web-vitals/reportEvents.d.ts.map +1 -1
- package/build/types/web-vitals/softNavs.d.ts.map +1 -1
- package/build/types/web-vitals/spans.d.ts +2 -3
- package/build/types/web-vitals/spans.d.ts.map +1 -1
- package/build/types/web-vitals/tracking.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,14 +10,17 @@
|
|
|
10
10
|
[](https://www.npmjs.com/package/@sentry/browser-utils)
|
|
11
11
|
[](https://www.npmjs.com/package/@sentry/browser-utils)
|
|
12
12
|
|
|
13
|
+
Shared browser utilities used by Sentry’s JavaScript SDKs.
|
|
14
|
+
|
|
13
15
|
> [!NOTE]
|
|
14
16
|
> This package is an internal library published for use by Sentry-owned JavaScript SDK packages. It is not part of the
|
|
15
17
|
> public API contract and may change in any release. Do not rely on SemVer compatibility if you depend on it directly.
|
|
16
18
|
|
|
17
|
-
##
|
|
19
|
+
## Documentation
|
|
18
20
|
|
|
19
|
-
- [
|
|
21
|
+
- [JavaScript SDK documentation](https://docs.sentry.io/platforms/javascript/)
|
|
20
22
|
|
|
21
|
-
##
|
|
23
|
+
## Support
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
- [Report a bug](https://github.com/getsentry/sentry-javascript/issues/new/choose)
|
|
26
|
+
- [Contributing](https://github.com/getsentry/sentry-javascript/blob/develop/CONTRIBUTING.md)
|
|
@@ -24,7 +24,7 @@ function startTrackingLongTasks() {
|
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
26
26
|
utils.startAndEndSpan(parent, startTime, startTime + duration, {
|
|
27
|
-
name:
|
|
27
|
+
name: core.UI_LONG_TASK_SPAN_NAME_FALLBACK,
|
|
28
28
|
op: op.UI_LONG_TASK,
|
|
29
29
|
attributes: {
|
|
30
30
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
@@ -69,7 +69,7 @@ function startTrackingLongAnimationFrames() {
|
|
|
69
69
|
attributes$1["browser.script.source_char_position"] = sourceCharPosition;
|
|
70
70
|
}
|
|
71
71
|
utils.startAndEndSpan(parent, startTime, startTime + duration, {
|
|
72
|
-
name:
|
|
72
|
+
name: core.UI_LONG_TASK_SPAN_NAME_FALLBACK,
|
|
73
73
|
op: op.UI_LONG_ANIMATION_FRAME,
|
|
74
74
|
attributes: attributes$1
|
|
75
75
|
});
|
|
@@ -101,7 +101,7 @@ function addPerformanceEntries(span, options) {
|
|
|
101
101
|
}
|
|
102
102
|
switch (entry.entryType) {
|
|
103
103
|
case "navigation": {
|
|
104
|
-
_addNavigationSpans(span, entry, timeOrigin);
|
|
104
|
+
_addNavigationSpans(span, entry, timeOrigin, spanStreamingEnabled);
|
|
105
105
|
break;
|
|
106
106
|
}
|
|
107
107
|
case "paint": {
|
|
@@ -129,23 +129,26 @@ function addPerformanceEntries(span, options) {
|
|
|
129
129
|
function _addPaintSpan(span, entry, startTime, duration, timeOrigin) {
|
|
130
130
|
const startTimestamp = timeOrigin + startTime;
|
|
131
131
|
utils.startAndEndSpan(span, startTimestamp, startTimestamp + duration, {
|
|
132
|
+
// The entry name (`first-paint`, `first-contentful-paint`) is already the low-cardinality name
|
|
133
|
+
// the conventions ask for, so only the attribute backing it has to be added.
|
|
132
134
|
name: entry.name,
|
|
133
135
|
attributes: {
|
|
134
136
|
[attributes.SENTRY_OP]: op.BROWSER_PAINT,
|
|
135
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.resource.browser.metrics"
|
|
137
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.resource.browser.metrics",
|
|
138
|
+
[attributes.BROWSER_PAINT_TYPE]: entry.name
|
|
136
139
|
}
|
|
137
140
|
});
|
|
138
141
|
}
|
|
139
|
-
function _addNavigationSpans(span, entry, timeOrigin) {
|
|
140
|
-
_addPerformanceNavigationTiming(span, entry, "unloadEvent", timeOrigin);
|
|
141
|
-
_addPerformanceNavigationTiming(span, entry, "redirect", timeOrigin);
|
|
142
|
-
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent", timeOrigin);
|
|
143
|
-
_addPerformanceNavigationTiming(span, entry, "loadEvent", timeOrigin);
|
|
144
|
-
_addPerformanceNavigationTiming(span, entry, "connect", timeOrigin);
|
|
145
|
-
_addPerformanceNavigationTiming(span, entry, "secureConnection", timeOrigin);
|
|
146
|
-
_addPerformanceNavigationTiming(span, entry, "fetch", timeOrigin);
|
|
147
|
-
_addPerformanceNavigationTiming(span, entry, "domainLookup", timeOrigin);
|
|
148
|
-
_addRequest(span, entry, timeOrigin);
|
|
142
|
+
function _addNavigationSpans(span, entry, timeOrigin, spanStreamingEnabled) {
|
|
143
|
+
_addPerformanceNavigationTiming(span, entry, "unloadEvent", timeOrigin, spanStreamingEnabled);
|
|
144
|
+
_addPerformanceNavigationTiming(span, entry, "redirect", timeOrigin, spanStreamingEnabled);
|
|
145
|
+
_addPerformanceNavigationTiming(span, entry, "domContentLoadedEvent", timeOrigin, spanStreamingEnabled);
|
|
146
|
+
_addPerformanceNavigationTiming(span, entry, "loadEvent", timeOrigin, spanStreamingEnabled);
|
|
147
|
+
_addPerformanceNavigationTiming(span, entry, "connect", timeOrigin, spanStreamingEnabled);
|
|
148
|
+
_addPerformanceNavigationTiming(span, entry, "secureConnection", timeOrigin, spanStreamingEnabled);
|
|
149
|
+
_addPerformanceNavigationTiming(span, entry, "fetch", timeOrigin, spanStreamingEnabled);
|
|
150
|
+
_addPerformanceNavigationTiming(span, entry, "domainLookup", timeOrigin, spanStreamingEnabled);
|
|
151
|
+
_addRequest(span, entry, timeOrigin, spanStreamingEnabled);
|
|
149
152
|
}
|
|
150
153
|
const NAVIGATION_TIMING_SPAN_OPS = {
|
|
151
154
|
secureConnection: op.BROWSER_TLS_SSL,
|
|
@@ -157,18 +160,22 @@ const NAVIGATION_TIMING_SPAN_OPS = {
|
|
|
157
160
|
domContentLoadedEvent: op.BROWSER_DOM_CONTENT_LOADED_EVENT,
|
|
158
161
|
loadEvent: op.BROWSER_LOAD_EVENT
|
|
159
162
|
};
|
|
160
|
-
function _addPerformanceNavigationTiming(span, entry, event, timeOrigin) {
|
|
163
|
+
function _addPerformanceNavigationTiming(span, entry, event, timeOrigin, spanStreamingEnabled) {
|
|
161
164
|
const eventEnd = _getEndPropertyNameForNavigationTiming(event);
|
|
162
165
|
const end = entry[eventEnd];
|
|
163
166
|
const start = entry[`${event}Start`];
|
|
164
167
|
if (!start || !end) {
|
|
165
168
|
return;
|
|
166
169
|
}
|
|
170
|
+
const op = NAVIGATION_TIMING_SPAN_OPS[event];
|
|
167
171
|
utils.startAndEndSpan(span, timeOrigin + utils.msToSec(start), timeOrigin + utils.msToSec(end), {
|
|
168
|
-
|
|
172
|
+
// With span streaming, span names have to be low cardinality, so we can't fall back to the
|
|
173
|
+
// document URL. `url.full` keeps it, and is what Relay derives the description from.
|
|
174
|
+
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op] : entry.name,
|
|
169
175
|
attributes: {
|
|
170
|
-
[attributes.SENTRY_OP]:
|
|
176
|
+
[attributes.SENTRY_OP]: op,
|
|
171
177
|
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
178
|
+
[attributes.URL_FULL]: core.filterCollectedUrl(entry.name),
|
|
172
179
|
...event === "redirect" && entry.redirectCount != null ? { "http.redirect_count": entry.redirectCount } : {}
|
|
173
180
|
}
|
|
174
181
|
});
|
|
@@ -182,23 +189,26 @@ function _getEndPropertyNameForNavigationTiming(event) {
|
|
|
182
189
|
}
|
|
183
190
|
return `${event}End`;
|
|
184
191
|
}
|
|
185
|
-
function _addRequest(span, entry, timeOrigin) {
|
|
192
|
+
function _addRequest(span, entry, timeOrigin, spanStreamingEnabled) {
|
|
186
193
|
const requestStartTimestamp = timeOrigin + utils.msToSec(entry.requestStart);
|
|
187
194
|
const responseEndTimestamp = timeOrigin + utils.msToSec(entry.responseEnd);
|
|
188
195
|
const responseStartTimestamp = timeOrigin + utils.msToSec(entry.responseStart);
|
|
189
196
|
if (entry.responseEnd) {
|
|
197
|
+
const url = core.filterCollectedUrl(entry.name);
|
|
190
198
|
utils.startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {
|
|
191
|
-
name: entry.name,
|
|
199
|
+
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op.BROWSER_REQUEST] : entry.name,
|
|
192
200
|
attributes: {
|
|
193
201
|
[attributes.SENTRY_OP]: op.BROWSER_REQUEST,
|
|
194
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
202
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
203
|
+
[attributes.URL_FULL]: url
|
|
195
204
|
}
|
|
196
205
|
});
|
|
197
206
|
utils.startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {
|
|
198
|
-
name: entry.name,
|
|
207
|
+
name: spanStreamingEnabled ? core.BROWSER_NAVIGATION_TIMING_SPAN_NAMES[op.BROWSER_RESPONSE] : entry.name,
|
|
199
208
|
attributes: {
|
|
200
209
|
[attributes.SENTRY_OP]: op.BROWSER_RESPONSE,
|
|
201
|
-
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics"
|
|
210
|
+
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.ui.browser.metrics",
|
|
211
|
+
[attributes.URL_FULL]: url
|
|
202
212
|
}
|
|
203
213
|
});
|
|
204
214
|
}
|
|
@@ -298,6 +308,7 @@ function _setResourceRequestAttributes(entry, attributes, properties) {
|
|
|
298
308
|
}
|
|
299
309
|
|
|
300
310
|
exports._addNavigationSpans = _addNavigationSpans;
|
|
311
|
+
exports._addPaintSpan = _addPaintSpan;
|
|
301
312
|
exports._addResourceSpans = _addResourceSpans;
|
|
302
313
|
exports._setResourceRequestAttributes = _setResourceRequestAttributes;
|
|
303
314
|
exports.addPerformanceEntries = addPerformanceEntries;
|
|
@@ -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 browserPerformanceTimeOrigin,\n getActiveSpan,\n parseUrl,\n RESOURCE_SPAN_NAME_FALLBACK,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n setMeasurement,\n spanToJSON,\n filterCollectedUrl,\n} from '@sentry/core';\nimport {\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: 'Main UI thread blocked',\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: 'Main UI thread blocked',\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);\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/** Create a span for a browser paint performance entry. */\nfunction _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 name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_PAINT,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics',\n },\n });\n}\n\n/**\n * Instrument navigation entries\n * exported only for tests\n */\nexport function _addNavigationSpans(span: Span, entry: PerformanceNavigationTiming, timeOrigin: number): void {\n _addPerformanceNavigationTiming(span, entry, 'unloadEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'redirect', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'domContentLoadedEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'loadEvent', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'connect', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'secureConnection', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'fetch', timeOrigin);\n _addPerformanceNavigationTiming(span, entry, 'domainLookup', timeOrigin);\n\n _addRequest(span, entry, timeOrigin);\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: Record<StartEventName, string> = {\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};\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): 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 startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: NAVIGATION_TIMING_SPAN_OPS[event],\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\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, timeOrigin: number): 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 startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_REQUEST,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\n },\n });\n\n startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, {\n name: entry.name,\n attributes: {\n [SENTRY_OP]: BROWSER_RESPONSE,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics',\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","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","attributes","CODE_FILE_PATH","CODE_FUNCTION_NAME","UI_LONG_ANIMATION_FRAME","getBrowserPerformanceAPI","BROWSER_PAINT","BROWSER_TLS_SSL","BROWSER_CACHE","BROWSER_DNS","BROWSER_UNLOAD_EVENT","BROWSER_REDIRECT","BROWSER_CONNECT","BROWSER_DOM_CONTENT_LOADED_EVENT","BROWSER_LOAD_EVENT","BROWSER_REQUEST","BROWSER_RESPONSE","op","RESOURCE_OTHER","parseUrl","URL_SCHEME","SERVER_ADDRESS","URL_DOMAIN","HTTP_REQUEST_SAME_ORIGIN","WINDOW","URL_FULL","filterCollectedUrl","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 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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"}
|
|
@@ -7,6 +7,7 @@ const browser = require('@sentry/core/browser');
|
|
|
7
7
|
const debugBuild = require('../debug-build.js');
|
|
8
8
|
const htmlTreeAsString = require('../htmlTreeAsString.js');
|
|
9
9
|
const performanceObserver = require('../instrumentation/performanceObserver.js');
|
|
10
|
+
const inp = require('../web-vitals/inp.js');
|
|
10
11
|
const isBotUserAgent = require('../isBotUserAgent.js');
|
|
11
12
|
const types = require('../types.js');
|
|
12
13
|
const utils = require('./utils.js');
|
|
@@ -20,12 +21,25 @@ const _interactionsIntegration = ((options = {}) => {
|
|
|
20
21
|
if (isBotUserAgent.isBotUserAgent()) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
|
-
const latestRoute = {
|
|
24
|
+
const latestRoute = {
|
|
25
|
+
name: void 0,
|
|
26
|
+
source: void 0,
|
|
27
|
+
routeId: void 0,
|
|
28
|
+
urlTemplate: void 0,
|
|
29
|
+
httpRoute: void 0,
|
|
30
|
+
urlPath: void 0,
|
|
31
|
+
urlFull: void 0
|
|
32
|
+
};
|
|
24
33
|
let inflightRouteSpan;
|
|
25
34
|
function trackRoute(span) {
|
|
26
35
|
const { name, attributes: attributes$1 } = core.spanToJSON(span);
|
|
27
36
|
latestRoute.name = name;
|
|
28
37
|
latestRoute.source = attributes$1[attributes.SENTRY_SOURCE] || attributes$1[attributes.SENTRY_SEGMENT_NAME_SOURCE];
|
|
38
|
+
latestRoute.routeId = attributes$1[attributes.ROUTER_NAVIGATION_ROUTE_ID];
|
|
39
|
+
latestRoute.urlTemplate = attributes$1[attributes.URL_TEMPLATE];
|
|
40
|
+
latestRoute.httpRoute = attributes$1[attributes.HTTP_ROUTE];
|
|
41
|
+
latestRoute.urlPath = attributes$1[attributes.URL_PATH];
|
|
42
|
+
latestRoute.urlFull = attributes$1[attributes.URL_FULL];
|
|
29
43
|
}
|
|
30
44
|
client.on("spanStart", (span) => {
|
|
31
45
|
if (isRouteSpan(span)) {
|
|
@@ -40,8 +54,8 @@ const _interactionsIntegration = ((options = {}) => {
|
|
|
40
54
|
inflightRouteSpan = void 0;
|
|
41
55
|
trackRoute(span);
|
|
42
56
|
});
|
|
43
|
-
registerInteractionListener(latestRoute, () => inflightRouteSpan, options);
|
|
44
|
-
trackInteractionsAsSpans();
|
|
57
|
+
registerInteractionListener(client, latestRoute, () => inflightRouteSpan, options);
|
|
58
|
+
trackInteractionsAsSpans(client);
|
|
45
59
|
}
|
|
46
60
|
};
|
|
47
61
|
});
|
|
@@ -53,7 +67,7 @@ function isRouteSpan(span) {
|
|
|
53
67
|
const op = core.spanToJSON(span).attributes[attributes.SENTRY_OP];
|
|
54
68
|
return op === "pageload" || op === "navigation";
|
|
55
69
|
}
|
|
56
|
-
function registerInteractionListener(latestRoute, getInflightRouteSpan, idleSpanOptions) {
|
|
70
|
+
function registerInteractionListener(client, latestRoute, getInflightRouteSpan, idleSpanOptions) {
|
|
57
71
|
if (!types.WINDOW.document) {
|
|
58
72
|
return;
|
|
59
73
|
}
|
|
@@ -76,13 +90,23 @@ function registerInteractionListener(latestRoute, getInflightRouteSpan, idleSpan
|
|
|
76
90
|
debugBuild.DEBUG_BUILD && core.debug.warn(`[Tracing] Did not create ${op.UI_ACTION_CLICK} span because the latest route name is missing.`);
|
|
77
91
|
return;
|
|
78
92
|
}
|
|
93
|
+
const hasSpanStreaming = core.hasSpanStreamingEnabled(client);
|
|
94
|
+
const routeName = latestRoute.routeId || latestRoute.urlTemplate || latestRoute.httpRoute;
|
|
95
|
+
const streamedName = routeName || core.UI_ACTION_CLICK_SPAN_NAME_FALLBACK;
|
|
96
|
+
const streamedNameSource = !routeName || latestRoute.routeId ? "custom" : "route";
|
|
79
97
|
inflightInteractionSpan = browser.startIdleSpan(
|
|
80
98
|
{
|
|
81
|
-
name: latestRoute.name,
|
|
99
|
+
name: hasSpanStreaming ? streamedName : latestRoute.name,
|
|
82
100
|
attributes: {
|
|
83
101
|
[attributes.SENTRY_OP]: op.UI_ACTION_CLICK,
|
|
84
|
-
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: latestRoute.source || "url",
|
|
85
|
-
[attributes.SENTRY_ORIGIN]: "auto.browser.interactions"
|
|
102
|
+
[attributes.SENTRY_SEGMENT_NAME_SOURCE]: hasSpanStreaming ? streamedNameSource : latestRoute.source || "url",
|
|
103
|
+
[attributes.SENTRY_ORIGIN]: "auto.browser.interactions",
|
|
104
|
+
...hasSpanStreaming && { [attributes.SENTRY_SEGMENT_NAME]: streamedName },
|
|
105
|
+
...latestRoute.routeId && { [attributes.ROUTER_NAVIGATION_ROUTE_ID]: latestRoute.routeId },
|
|
106
|
+
...latestRoute.urlTemplate && { [attributes.URL_TEMPLATE]: latestRoute.urlTemplate },
|
|
107
|
+
...latestRoute.httpRoute && { [attributes.HTTP_ROUTE]: latestRoute.httpRoute },
|
|
108
|
+
...latestRoute.urlPath && { [attributes.URL_PATH]: latestRoute.urlPath },
|
|
109
|
+
...latestRoute.urlFull && { [attributes.URL_FULL]: core.filterCollectedUrl(latestRoute.urlFull) }
|
|
86
110
|
}
|
|
87
111
|
},
|
|
88
112
|
idleSpanOptions
|
|
@@ -91,7 +115,7 @@ function registerInteractionListener(latestRoute, getInflightRouteSpan, idleSpan
|
|
|
91
115
|
{ capture: true }
|
|
92
116
|
);
|
|
93
117
|
}
|
|
94
|
-
function trackInteractionsAsSpans() {
|
|
118
|
+
function trackInteractionsAsSpans(client) {
|
|
95
119
|
performanceObserver.addPerformanceInstrumentationHandler("event", ({ entries }) => {
|
|
96
120
|
const parent = core.getActiveSpan();
|
|
97
121
|
if (!parent) {
|
|
@@ -101,15 +125,18 @@ function trackInteractionsAsSpans() {
|
|
|
101
125
|
if (entry.name === "click") {
|
|
102
126
|
const startTime = utils.msToSec(core.browserPerformanceTimeOrigin() + entry.startTime);
|
|
103
127
|
const duration = utils.msToSec(entry.duration);
|
|
128
|
+
const selector = htmlTreeAsString.htmlTreeAsString(entry.target);
|
|
129
|
+
const componentName$1 = componentName.getComponentName(entry.target);
|
|
130
|
+
const hasSpanStreaming = core.hasSpanStreamingEnabled(client);
|
|
104
131
|
const spanOptions = {
|
|
105
|
-
name:
|
|
132
|
+
name: hasSpanStreaming ? componentName$1 || core.UI_INTERACTION_CLICK_SPAN_NAME_FALLBACK : selector,
|
|
106
133
|
startTime,
|
|
107
134
|
attributes: {
|
|
108
135
|
[attributes.SENTRY_OP]: op.UI_INTERACTION_CLICK,
|
|
109
|
-
[attributes.SENTRY_ORIGIN]: "auto.browser.interactions"
|
|
136
|
+
[attributes.SENTRY_ORIGIN]: "auto.browser.interactions",
|
|
137
|
+
...selector && selector !== inp.UNKNOWN_ELEMENT_NAME && { [attributes.BROWSER_WEB_VITAL_INP_TARGET]: selector }
|
|
110
138
|
}
|
|
111
139
|
};
|
|
112
|
-
const componentName$1 = componentName.getComponentName(entry.target);
|
|
113
140
|
if (componentName$1) {
|
|
114
141
|
spanOptions.attributes[attributes.UI_COMPONENT_NAME] = componentName$1;
|
|
115
142
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions.js","sources":["../../../src/performance/interactions.ts"],"sourcesContent":["import {\n SENTRY_OP,\n SENTRY_ORIGIN,\n SENTRY_IDLE_SPAN_FINISH_REASON,\n UI_COMPONENT_NAME,\n SENTRY_SOURCE,\n SENTRY_SEGMENT_NAME_SOURCE,\n} from '@sentry/conventions/attributes';\nimport { UI_INTERACTION_CLICK, UI_ACTION_CLICK } from '@sentry/conventions/op';\nimport type { IntegrationFn, Span, StartSpanOptions, TransactionSource } from '@sentry/core';\nimport {\n browserPerformanceTimeOrigin,\n debug,\n defineIntegration,\n getActiveSpan,\n getRootSpan,\n spanToJSON,\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 { 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}\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 = { name: undefined, source: undefined };\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 }\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(latestRoute, () => inflightRouteSpan, options);\n\n trackInteractionsAsSpans();\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 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 inflightInteractionSpan = startIdleSpan(\n {\n name: latestRoute.name,\n attributes: {\n [SENTRY_OP]: UI_ACTION_CLICK,\n [SENTRY_SEGMENT_NAME_SOURCE]: latestRoute.source || 'url',\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\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(): 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 spanOptions: StartSpanOptions & Required<Pick<StartSpanOptions, 'attributes'>> = {\n name: htmlTreeAsString(entry.target),\n startTime: startTime,\n attributes: {\n [SENTRY_OP]: UI_INTERACTION_CLICK,\n [SENTRY_ORIGIN]: 'auto.browser.interactions',\n },\n };\n\n const componentName = getComponentName(entry.target);\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","defineIntegration","getRootSpan","SENTRY_OP","WINDOW","DEBUG_BUILD","debug","UI_ACTION_CLICK","SENTRY_IDLE_SPAN_FINISH_REASON","startIdleSpan","SENTRY_ORIGIN","addPerformanceInstrumentationHandler","getActiveSpan","msToSec","browserPerformanceTimeOrigin","htmlTreeAsString","UI_INTERACTION_CLICK","componentName","getComponentName","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 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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"}
|
|
@@ -14,7 +14,6 @@ function _emitWebVitalSpan(options) {
|
|
|
14
14
|
value,
|
|
15
15
|
attributes: passedAttributes,
|
|
16
16
|
parentSpan,
|
|
17
|
-
reportEvent,
|
|
18
17
|
startTime,
|
|
19
18
|
endTime,
|
|
20
19
|
standalone,
|
|
@@ -38,9 +37,6 @@ function _emitWebVitalSpan(options) {
|
|
|
38
37
|
if (parentSpan && core.spanToJSON(parentSpan).attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_OP] === "pageload") {
|
|
39
38
|
attributes$1["sentry.pageload.span_id"] = parentSpan.spanContext().spanId;
|
|
40
39
|
}
|
|
41
|
-
if (reportEvent) {
|
|
42
|
-
attributes$1[`browser.web_vital.${metricName}.report_event`] = reportEvent;
|
|
43
|
-
}
|
|
44
40
|
if (softNavigationId != null) {
|
|
45
41
|
attributes$1[attributes.BROWSER_NAVIGATION_ID] = softNavigationId;
|
|
46
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitSpan.js","sources":["../../../src/web-vitals/emitSpan.ts"],"sourcesContent":["import type { Integration, Span, SpanAttributes } from '@sentry/core';\nimport {\n getClient,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { startInactiveSpan } from '@sentry/core/browser';\nimport {\n BROWSER_NAVIGATION_ID,\n BROWSER_NAVIGATION_TYPE,\n SENTRY_REPLAY_ID,\n SENTRY_SEGMENT_NAME,\n SENTRY_TRANSACTION,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\nimport { WINDOW } from '../types';\nimport type { MetricNavigationType } from '../instrumentation/performanceObserver';\
|
|
1
|
+
{"version":3,"file":"emitSpan.js","sources":["../../../src/web-vitals/emitSpan.ts"],"sourcesContent":["import type { Integration, Span, SpanAttributes } from '@sentry/core';\nimport {\n getClient,\n getCurrentScope,\n getRootSpan,\n SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n spanToJSON,\n} from '@sentry/core';\nimport { startInactiveSpan } from '@sentry/core/browser';\nimport {\n BROWSER_NAVIGATION_ID,\n BROWSER_NAVIGATION_TYPE,\n SENTRY_REPLAY_ID,\n SENTRY_SEGMENT_NAME,\n SENTRY_TRANSACTION,\n USER_AGENT_ORIGINAL,\n} from '@sentry/conventions/attributes';\nimport { WINDOW } from '../types';\nimport type { MetricNavigationType } from '../instrumentation/performanceObserver';\n\n// Locally-defined interfaces to avoid leaking bare global type references into the\n// generated .d.ts. The `declare global` augmentations in web-vitals/types.ts make these\n// available during this package's compilation but are NOT carried to consumers.\n// This mirrors the pattern used for PerformanceEventTiming in instrument.ts.\nexport interface LayoutShift extends PerformanceEntry {\n value: number;\n sources: Array<{ node: Node | null }>;\n hadRecentInput: boolean;\n}\n\nexport interface LargestContentfulPaint extends PerformanceEntry {\n readonly renderTime: DOMHighResTimeStamp;\n readonly loadTime: DOMHighResTimeStamp;\n readonly size: number;\n readonly id: string;\n readonly url: string;\n readonly element: Element | null;\n}\n\ninterface WebVitalSpanOptions {\n name: string;\n op: string;\n origin: string;\n metricName: 'lcp' | 'cls' | 'inp';\n value: number;\n attributes?: SpanAttributes;\n parentSpan?: Span;\n startTime: number;\n endTime?: number;\n /** Set when the vital was reported for a soft navigation rather than the initial page load. */\n softNavigationId?: number;\n /** The navigation the vital was measured on, as reported by web-vitals. */\n navigationType?: MetricNavigationType;\n /**\n * When `true`, the span is sent on its own as a v2 streamed span instead of being folded into a\n * transaction. Used for INP when span streaming is disabled (it reports late, so it can't ride\n * the pageload transaction).\n *\n * TODO(standalone): remove once the static (transaction) trace lifecycle is dropped and INP always streams.\n */\n standalone?: boolean;\n}\n\n/**\n * Emits a web vital span. When `standalone` is set it is sent on its own as a v2 streamed span;\n * otherwise it flows through the span streaming pipeline as a child of `parentSpan`.\n */\nexport function _emitWebVitalSpan(options: WebVitalSpanOptions): void {\n const {\n name,\n op,\n origin,\n metricName,\n value,\n attributes: passedAttributes,\n parentSpan,\n startTime,\n endTime,\n standalone,\n softNavigationId,\n navigationType,\n } = options;\n\n // Taken off the segment span itself, so it can't diverge from it: a routing instrumentation may\n // rename that span (a pageload span is named `Pageload` until its route resolves), and the scope's\n // transaction name is deliberately not kept in sync with it. Only a standalone span, which is sent\n // without its segment span, has to fall back to the scope.\n const segmentSpan = parentSpan && getRootSpan(parentSpan);\n const segmentName = segmentSpan ? spanToJSON(segmentSpan).name : getCurrentScope().getScopeData().transactionName;\n\n const attributes: SpanAttributes = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: origin,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: op,\n [SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME]: 0,\n [`browser.web_vital.${metricName}.value`]: value,\n // oxlint-disable-next-line typescript-eslint/no-deprecated\n [SENTRY_TRANSACTION]: segmentName,\n [SENTRY_SEGMENT_NAME]: segmentName,\n // Web vital score calculation relies on the user agent\n [USER_AGENT_ORIGINAL]: WINDOW.navigator?.userAgent,\n ...passedAttributes,\n };\n\n if (parentSpan && spanToJSON(parentSpan).attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'pageload') {\n // for LCP and CLS, we collect the pageload span id as an attribute\n attributes['sentry.pageload.span_id'] = parentSpan.spanContext().spanId;\n }\n\n if (softNavigationId != null) {\n attributes[BROWSER_NAVIGATION_ID] = softNavigationId;\n }\n\n if (navigationType) {\n attributes[BROWSER_NAVIGATION_TYPE] = navigationType;\n }\n\n // A standalone span is sent as a plain v2 span without running the `processSpan` hooks (see\n // `captureStandaloneSpanWithStaticCallback`), so Replay can't attach the replay id itself. Set it\n // here, mirroring Replay's `processSpan`, so INP keeps its replay association like it did on v1.\n // TODO(standalone): remove once the static (transaction) trace lifecycle is dropped and INP always\n // streams, at which point Replay's `processSpan` runs and attaches the replay id.\n if (standalone) {\n Object.assign(attributes, getReplayAttributes());\n }\n\n const span = startInactiveSpan({\n name,\n attributes,\n startTime,\n parentSpan,\n // oxlint-disable-next-line typescript/no-deprecated -- intentional during the v1/v2 transition; see the TODO(standalone) above\n experimental: standalone ? { standalone: true } : undefined,\n });\n\n if (span) {\n span.end(endTime ?? startTime);\n }\n}\n\ninterface ReplayIntegration extends Integration {\n getReplayId: (onlyIfSampled?: boolean) => string | undefined;\n getRecordingMode: () => 'session' | 'buffer' | undefined;\n}\n\n// TODO(standalone): remove once the static (transaction) trace lifecycle is dropped; Replay's\n// `processSpan` then attaches the replay id to the streamed INP span instead.\nfunction getReplayAttributes(): SpanAttributes {\n const replay = getClient()?.getIntegrationByName<ReplayIntegration>('Replay');\n const replayId = replay?.getReplayId(true);\n if (!replayId) {\n return {};\n }\n\n return {\n [SENTRY_REPLAY_ID]: replayId,\n // Not the `SENTRY_REPLAY_IS_BUFFERING` convention: that one has no `sentry._internal.` prefix, and\n // the rest of the SDK sets the prefixed key (see `logs/internal.ts`, `metrics/internal.ts`).\n 'sentry._internal.replay_is_buffering': replay!.getRecordingMode() === 'buffer' ? true : undefined,\n };\n}\n"],"names":["getRootSpan","spanToJSON","getCurrentScope","attributes","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME","SENTRY_TRANSACTION","SENTRY_SEGMENT_NAME","USER_AGENT_ORIGINAL","WINDOW","BROWSER_NAVIGATION_ID","BROWSER_NAVIGATION_TYPE","startInactiveSpan","getClient","SENTRY_REPLAY_ID"],"mappings":";;;;;;;AAqEO,SAAS,kBAAkB,OAAA,EAAoC;AACpE,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,EAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA,EAAY,gBAAA;AAAA,IACZ,UAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAMJ,EAAA,MAAM,WAAA,GAAc,UAAA,IAAcA,gBAAA,CAAY,UAAU,CAAA;AACxD,EAAA,MAAM,WAAA,GAAc,cAAcC,eAAA,CAAW,WAAW,EAAE,IAAA,GAAOC,oBAAA,EAAgB,CAAE,YAAA,EAAa,CAAE,eAAA;AAElG,EAAA,MAAMC,YAAA,GAA6B;AAAA,IACjC,CAACC,qCAAgC,GAAG,MAAA;AAAA,IACpC,CAACC,iCAA4B,GAAG,EAAA;AAAA,IAChC,CAACC,sCAAiC,GAAG,CAAA;AAAA,IACrC,CAAC,CAAA,kBAAA,EAAqB,UAAU,CAAA,MAAA,CAAQ,GAAG,KAAA;AAAA;AAAA,IAE3C,CAACC,6BAAkB,GAAG,WAAA;AAAA,IACtB,CAACC,8BAAmB,GAAG,WAAA;AAAA;AAAA,IAEvB,CAACC,8BAAmB,GAAGC,YAAA,CAAO,SAAA,EAAW,SAAA;AAAA,IACzC,GAAG;AAAA,GACL;AAEA,EAAA,IAAI,cAAcT,eAAA,CAAW,UAAU,EAAE,UAAA,CAAWI,iCAA4B,MAAM,UAAA,EAAY;AAEhG,IAAAF,YAAA,CAAW,yBAAyB,CAAA,GAAI,UAAA,CAAW,WAAA,EAAY,CAAE,MAAA;AAAA,EACnE;AAEA,EAAA,IAAI,oBAAoB,IAAA,EAAM;AAC5B,IAAAA,YAAA,CAAWQ,gCAAqB,CAAA,GAAI,gBAAA;AAAA,EACtC;AAEA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAAR,YAAA,CAAWS,kCAAuB,CAAA,GAAI,cAAA;AAAA,EACxC;AAOA,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,MAAA,CAAO,MAAA,CAAOT,YAAA,EAAY,mBAAA,EAAqB,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,OAAOU,yBAAA,CAAkB;AAAA,IAC7B,IAAA;AAAA,gBACAV,YAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA;AAAA,IAEA,YAAA,EAAc,UAAA,GAAa,EAAE,UAAA,EAAY,MAAK,GAAI;AAAA,GACnD,CAAA;AAED,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,IAAA,CAAK,GAAA,CAAI,WAAW,SAAS,CAAA;AAAA,EAC/B;AACF;AASA,SAAS,mBAAA,GAAsC;AAC7C,EAAA,MAAM,MAAA,GAASW,cAAA,EAAU,EAAG,oBAAA,CAAwC,QAAQ,CAAA;AAC5E,EAAA,MAAM,QAAA,GAAW,MAAA,EAAQ,WAAA,CAAY,IAAI,CAAA;AACzC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,OAAO;AAAA,IACL,CAACC,2BAAgB,GAAG,QAAA;AAAA;AAAA;AAAA,IAGpB,sCAAA,EAAwC,MAAA,CAAQ,gBAAA,EAAiB,KAAM,WAAW,IAAA,GAAO;AAAA,GAC3F;AACF;;;;"}
|
|
@@ -115,6 +115,7 @@ function registerInpInteractionListener() {
|
|
|
115
115
|
|
|
116
116
|
exports.INP_ENTRY_MAP = INP_ENTRY_MAP;
|
|
117
117
|
exports.MAX_PLAUSIBLE_INP_DURATION = MAX_PLAUSIBLE_INP_DURATION;
|
|
118
|
+
exports.UNKNOWN_ELEMENT_NAME = UNKNOWN_ELEMENT_NAME;
|
|
118
119
|
exports.getCachedInteractionContext = getCachedInteractionContext;
|
|
119
120
|
exports.registerInpInteractionListener = registerInpInteractionListener;
|
|
120
121
|
//# sourceMappingURL=inp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inp.js","sources":["../../../src/web-vitals/inp.ts"],"sourcesContent":["import type { Span } from '@sentry/core';\nimport { getActiveSpan, getRootSpan, isBrowser } from '@sentry/core';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport { WINDOW } from '../types';\nimport { addPerformanceInstrumentationHandler, isPerformanceEventTiming } from '../instrumentation/performanceObserver';\n\ninterface InteractionContext {\n span: Span | undefined;\n elementName: string;\n}\n\nconst LAST_INTERACTIONS: number[] = [];\nconst INTERACTIONS_SPAN_MAP = new Map<number, InteractionContext>();\n\n// Map to store element names by timestamp, since we get the DOM event before the PerformanceObserver entry\n/** What `htmlTreeAsString` returns when it cannot describe the target. */\
|
|
1
|
+
{"version":3,"file":"inp.js","sources":["../../../src/web-vitals/inp.ts"],"sourcesContent":["import type { Span } from '@sentry/core';\nimport { getActiveSpan, getRootSpan, isBrowser } from '@sentry/core';\nimport { htmlTreeAsString } from '../htmlTreeAsString';\nimport { WINDOW } from '../types';\nimport { addPerformanceInstrumentationHandler, isPerformanceEventTiming } from '../instrumentation/performanceObserver';\n\ninterface InteractionContext {\n span: Span | undefined;\n elementName: string;\n}\n\nconst LAST_INTERACTIONS: number[] = [];\nconst INTERACTIONS_SPAN_MAP = new Map<number, InteractionContext>();\n\n// Map to store element names by timestamp, since we get the DOM event before the PerformanceObserver entry\n/** What `htmlTreeAsString` returns when it cannot describe the target. */\nexport const UNKNOWN_ELEMENT_NAME = '<unknown>';\n\nconst ELEMENT_NAME_TIMESTAMP_MAP = new Map<number, string>();\n\n/**\n * 60 seconds is the maximum for a plausible INP value\n * (source: Me)\n */\nexport const MAX_PLAUSIBLE_INP_DURATION = 60;\n\nexport type InteractionType = 'click' | 'hover' | 'drag' | 'press';\n\nexport const INP_ENTRY_MAP: Record<string, InteractionType> = {\n click: 'click',\n pointerdown: 'click',\n pointerup: 'click',\n mousedown: 'click',\n mouseup: 'click',\n touchstart: 'click',\n touchend: 'click',\n mouseover: 'hover',\n mouseout: 'hover',\n mouseenter: 'hover',\n mouseleave: 'hover',\n pointerover: 'hover',\n pointerout: 'hover',\n pointerenter: 'hover',\n pointerleave: 'hover',\n dragstart: 'drag',\n dragend: 'drag',\n drag: 'drag',\n dragenter: 'drag',\n dragleave: 'drag',\n dragover: 'drag',\n drop: 'drag',\n keydown: 'press',\n keyup: 'press',\n keypress: 'press',\n input: 'press',\n};\n\n/**\n * Look up a cached interaction context (element name + root span) by interactionId.\n * Returns undefined if no context was cached for this interaction.\n */\nexport function getCachedInteractionContext(interactionId: number | undefined): InteractionContext | undefined {\n return interactionId != null ? INTERACTIONS_SPAN_MAP.get(interactionId) : undefined;\n}\n\n/**\n * Register a listener to cache route information for INP interactions.\n */\nexport function registerInpInteractionListener(): void {\n // Listen for all interaction events that could contribute to INP\n const interactionEvents = Object.keys(INP_ENTRY_MAP);\n if (isBrowser()) {\n interactionEvents.forEach(eventType => {\n WINDOW.addEventListener(eventType, captureElementFromEvent, { capture: true, passive: true });\n });\n }\n\n /**\n * Captures the element name from a DOM event and stores it in the ELEMENT_NAME_TIMESTAMP_MAP.\n */\n function captureElementFromEvent(event: Event): void {\n const target = event.target as HTMLElement | null;\n if (!target) {\n return;\n }\n\n const elementName = htmlTreeAsString(target);\n const timestamp = Math.round(event.timeStamp);\n\n // Not every event of an interaction has a describable target, and one that doesn't would\n // otherwise claim the timestamp and leave the span unnamed.\n if (!elementName || elementName === UNKNOWN_ELEMENT_NAME) {\n return;\n }\n\n // Every event of one interaction shares a timestamp, and so do the `pointerover`/`mouseover`\n // the browser fires afterwards when a handler swaps out the element under the cursor. Those\n // arrive last and describe the new DOM, so keeping the first usable name is what pins the entry\n // to the element that was actually interacted with.\n if (ELEMENT_NAME_TIMESTAMP_MAP.has(timestamp)) {\n return;\n }\n\n // Store the element name by timestamp so we can match it with the PerformanceEntry\n ELEMENT_NAME_TIMESTAMP_MAP.set(timestamp, elementName);\n\n // Clean up old\n if (ELEMENT_NAME_TIMESTAMP_MAP.size > 50) {\n const firstKey = ELEMENT_NAME_TIMESTAMP_MAP.keys().next().value;\n if (firstKey !== undefined) {\n ELEMENT_NAME_TIMESTAMP_MAP.delete(firstKey);\n }\n }\n }\n\n /**\n * Tries to get the element name from the timestamp map.\n */\n function resolveElementNameFromEntry(entry: PerformanceEntry): string {\n const timestamp = Math.round(entry.startTime);\n let elementName = ELEMENT_NAME_TIMESTAMP_MAP.get(timestamp);\n\n // try nearby timestamps (±5ms)\n if (!elementName) {\n for (let offset = -5; offset <= 5; offset++) {\n const nearbyName = ELEMENT_NAME_TIMESTAMP_MAP.get(timestamp + offset);\n if (nearbyName) {\n elementName = nearbyName;\n break;\n }\n }\n }\n\n return elementName || UNKNOWN_ELEMENT_NAME;\n }\n\n const handleEntries = ({ entries }: { entries: PerformanceEntry[] }): void => {\n const activeSpan = getActiveSpan();\n const activeRootSpan = activeSpan && getRootSpan(activeSpan);\n\n entries.forEach(entry => {\n if (!isPerformanceEventTiming(entry)) {\n return;\n }\n\n const interactionId = entry.interactionId;\n if (interactionId == null) {\n return;\n }\n\n // If the interaction was already recorded before, nothing more to do\n if (INTERACTIONS_SPAN_MAP.has(interactionId)) {\n return;\n }\n\n const elementName = entry.target ? htmlTreeAsString(entry.target) : resolveElementNameFromEntry(entry);\n\n // We keep max. 10 interactions in the list, then remove the oldest one & clean up\n if (LAST_INTERACTIONS.length > 10) {\n const last = LAST_INTERACTIONS.shift() as number;\n INTERACTIONS_SPAN_MAP.delete(last);\n }\n\n // We add the interaction to the list of recorded interactions\n // and store both the span and element name for this interaction\n LAST_INTERACTIONS.push(interactionId);\n INTERACTIONS_SPAN_MAP.set(interactionId, {\n span: activeRootSpan,\n elementName,\n });\n });\n };\n\n addPerformanceInstrumentationHandler('event', handleEntries);\n addPerformanceInstrumentationHandler('first-input', handleEntries);\n}\n"],"names":["isBrowser","WINDOW","htmlTreeAsString","getActiveSpan","getRootSpan","isPerformanceEventTiming","addPerformanceInstrumentationHandler"],"mappings":"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"}
|