Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
102 lines
4.1 KiB
Plaintext
102 lines
4.1 KiB
Plaintext
import { browserPerformanceTimeOrigin, getActiveSpan, getRootSpan, spanToJSON, getCurrentScope, timestampInSeconds, startSpan, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
|
|
import { addPerformanceInstrumentationHandler } from './instrument.js';
|
|
import { getBrowserPerformanceAPI, msToSec } from './utils.js';
|
|
|
|
// ElementTiming interface based on the W3C spec
|
|
|
|
/**
|
|
* Start tracking ElementTiming performance entries.
|
|
*/
|
|
function startTrackingElementTiming() {
|
|
const performance = getBrowserPerformanceAPI();
|
|
if (performance && browserPerformanceTimeOrigin()) {
|
|
return addPerformanceInstrumentationHandler('element', _onElementTiming);
|
|
}
|
|
|
|
return () => undefined;
|
|
}
|
|
|
|
/**
|
|
* exported only for testing
|
|
*/
|
|
const _onElementTiming = ({ entries }) => {
|
|
const activeSpan = getActiveSpan();
|
|
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
|
|
const transactionName = rootSpan
|
|
? spanToJSON(rootSpan).description
|
|
: getCurrentScope().getScopeData().transactionName;
|
|
|
|
entries.forEach(entry => {
|
|
const elementEntry = entry ;
|
|
|
|
// Skip entries without identifier (elementtiming attribute)
|
|
if (!elementEntry.identifier) {
|
|
return;
|
|
}
|
|
|
|
// `name` contains the type of the element paint. Can be `'image-paint'` or `'text-paint'`.
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming#instance_properties
|
|
const paintType = elementEntry.name ;
|
|
|
|
const renderTime = elementEntry.renderTime;
|
|
const loadTime = elementEntry.loadTime;
|
|
|
|
// starting the span at:
|
|
// - `loadTime` if available (should be available for all "image-paint" entries, 0 otherwise)
|
|
// - `renderTime` if available (available for all entries, except 3rd party images, but these should be covered by `loadTime`, 0 otherwise)
|
|
// - `timestampInSeconds()` as a safeguard
|
|
// see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming/renderTime#cross-origin_image_render_time
|
|
const [spanStartTime, spanStartTimeSource] = loadTime
|
|
? [msToSec(loadTime), 'load-time']
|
|
: renderTime
|
|
? [msToSec(renderTime), 'render-time']
|
|
: [timestampInSeconds(), 'entry-emission'];
|
|
|
|
const duration =
|
|
paintType === 'image-paint'
|
|
? // for image paints, we can acually get a duration because image-paint entries also have a `loadTime`
|
|
// and `renderTime`. `loadTime` is the time when the image finished loading and `renderTime` is the
|
|
// time when the image finished rendering.
|
|
msToSec(Math.max(0, (renderTime ?? 0) - (loadTime ?? 0)))
|
|
: // for `'text-paint'` entries, we can't get a duration because the `loadTime` is always zero.
|
|
0;
|
|
|
|
const attributes = {
|
|
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.elementtiming',
|
|
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.elementtiming',
|
|
// name must be user-entered, so we can assume low cardinality
|
|
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component',
|
|
// recording the source of the span start time, as it varies depending on available data
|
|
'sentry.span_start_time_source': spanStartTimeSource,
|
|
'sentry.transaction_name': transactionName,
|
|
'element.id': elementEntry.id,
|
|
'element.type': elementEntry.element?.tagName?.toLowerCase() || 'unknown',
|
|
'element.size':
|
|
elementEntry.naturalWidth && elementEntry.naturalHeight
|
|
? `${elementEntry.naturalWidth}x${elementEntry.naturalHeight}`
|
|
: undefined,
|
|
'element.render_time': renderTime,
|
|
'element.load_time': loadTime,
|
|
// `url` is `0`(number) for text paints (hence we fall back to undefined)
|
|
'element.url': elementEntry.url || undefined,
|
|
'element.identifier': elementEntry.identifier,
|
|
'element.paint_type': paintType,
|
|
};
|
|
|
|
startSpan(
|
|
{
|
|
name: `element[${elementEntry.identifier}]`,
|
|
attributes,
|
|
startTime: spanStartTime,
|
|
onlyIfParent: true,
|
|
},
|
|
span => {
|
|
span.end(spanStartTime + duration);
|
|
},
|
|
);
|
|
});
|
|
};
|
|
|
|
export { _onElementTiming, startTrackingElementTiming };
|
|
//# sourceMappingURL=elementTiming.js.map
|