123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- Object.defineProperty(exports, '__esModule', { value: true });
- const core = require('@sentry/core');
- const utils = require('@sentry/utils');
- const debugBuild = require('../../common/debug-build.js');
- const instrument = require('../instrument.js');
- const types = require('../types.js');
- const getVisibilityWatcher = require('../web-vitals/lib/getVisibilityWatcher.js');
- const utils$1 = require('./utils.js');
- const MAX_INT_AS_BYTES = 2147483647;
- function msToSec(time) {
- return time / 1000;
- }
- function getBrowserPerformanceAPI() {
-
- return types.WINDOW && types.WINDOW.addEventListener && types.WINDOW.performance;
- }
- let _performanceCursor = 0;
- let _measurements = {};
- let _lcpEntry;
- let _clsEntry;
- function startTrackingWebVitals() {
- const performance = getBrowserPerformanceAPI();
- if (performance && utils.browserPerformanceTimeOrigin) {
-
- if (performance.mark) {
- types.WINDOW.performance.mark('sentry-tracing-init');
- }
- const fidCallback = _trackFID();
- const clsCallback = _trackCLS();
- const lcpCallback = _trackLCP();
- return () => {
- fidCallback();
- clsCallback();
- lcpCallback();
- };
- }
- return () => undefined;
- }
- function startTrackingLongTasks() {
- instrument.addPerformanceInstrumentationHandler('longtask', ({ entries }) => {
- for (const entry of entries) {
-
- const transaction = core.getActiveTransaction() ;
- if (!transaction) {
- return;
- }
- const startTime = msToSec((utils.browserPerformanceTimeOrigin ) + entry.startTime);
- const duration = msToSec(entry.duration);
-
- transaction.startChild({
- description: 'Main UI thread blocked',
- op: 'ui.long-task',
- origin: 'auto.ui.browser.metrics',
- startTimestamp: startTime,
- endTimestamp: startTime + duration,
- });
- }
- });
- }
- function startTrackingInteractions() {
- instrument.addPerformanceInstrumentationHandler('event', ({ entries }) => {
- for (const entry of entries) {
-
- const transaction = core.getActiveTransaction() ;
- if (!transaction) {
- return;
- }
- if (entry.name === 'click') {
- const startTime = msToSec((utils.browserPerformanceTimeOrigin ) + entry.startTime);
- const duration = msToSec(entry.duration);
- const span = {
- description: utils.htmlTreeAsString(entry.target),
- op: `ui.interaction.${entry.name}`,
- origin: 'auto.ui.browser.metrics',
- startTimestamp: startTime,
- endTimestamp: startTime + duration,
- };
- const componentName = utils.getComponentName(entry.target);
- if (componentName) {
- span.attributes = { 'ui.component_name': componentName };
- }
-
- transaction.startChild(span);
- }
- }
- });
- }
- function _trackCLS() {
- return instrument.addClsInstrumentationHandler(({ metric }) => {
- const entry = metric.entries[metric.entries.length - 1];
- if (!entry) {
- return;
- }
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding CLS');
- _measurements['cls'] = { value: metric.value, unit: '' };
- _clsEntry = entry ;
- }, true);
- }
- function _trackLCP() {
- return instrument.addLcpInstrumentationHandler(({ metric }) => {
- const entry = metric.entries[metric.entries.length - 1];
- if (!entry) {
- return;
- }
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding LCP');
- _measurements['lcp'] = { value: metric.value, unit: 'millisecond' };
- _lcpEntry = entry ;
- }, true);
- }
- function _trackFID() {
- return instrument.addFidInstrumentationHandler(({ metric }) => {
- const entry = metric.entries[metric.entries.length - 1];
- if (!entry) {
- return;
- }
- const timeOrigin = msToSec(utils.browserPerformanceTimeOrigin );
- const startTime = msToSec(entry.startTime);
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding FID');
- _measurements['fid'] = { value: metric.value, unit: 'millisecond' };
- _measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' };
- });
- }
- function addPerformanceEntries(transaction) {
- const performance = getBrowserPerformanceAPI();
- if (!performance || !types.WINDOW.performance.getEntries || !utils.browserPerformanceTimeOrigin) {
-
- return;
- }
- debugBuild.DEBUG_BUILD && utils.logger.log('[Tracing] Adding & adjusting spans using Performance API');
- const timeOrigin = msToSec(utils.browserPerformanceTimeOrigin);
- const performanceEntries = performance.getEntries();
- let responseStartTimestamp;
- let requestStartTimestamp;
- const { op, start_timestamp: transactionStartTime } = core.spanToJSON(transaction);
-
- performanceEntries.slice(_performanceCursor).forEach((entry) => {
- const startTime = msToSec(entry.startTime);
- const duration = msToSec(entry.duration);
-
- if (transaction.op === 'navigation' && transactionStartTime && timeOrigin + startTime < transactionStartTime) {
- return;
- }
- switch (entry.entryType) {
- case 'navigation': {
- _addNavigationSpans(transaction, entry, timeOrigin);
- responseStartTimestamp = timeOrigin + msToSec(entry.responseStart);
- requestStartTimestamp = timeOrigin + msToSec(entry.requestStart);
- break;
- }
- case 'mark':
- case 'paint':
- case 'measure': {
- _addMeasureSpans(transaction, entry, startTime, duration, timeOrigin);
-
- const firstHidden = getVisibilityWatcher.getVisibilityWatcher();
-
- const shouldRecord = entry.startTime < firstHidden.firstHiddenTime;
- if (entry.name === 'first-paint' && shouldRecord) {
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding FP');
- _measurements['fp'] = { value: entry.startTime, unit: 'millisecond' };
- }
- if (entry.name === 'first-contentful-paint' && shouldRecord) {
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding FCP');
- _measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' };
- }
- break;
- }
- case 'resource': {
- _addResourceSpans(transaction, entry, entry.name , startTime, duration, timeOrigin);
- break;
- }
-
- }
- });
- _performanceCursor = Math.max(performanceEntries.length - 1, 0);
- _trackNavigator(transaction);
-
- if (op === 'pageload') {
- _addTtfbToMeasurements(_measurements, responseStartTimestamp, requestStartTimestamp, transactionStartTime);
- ['fcp', 'fp', 'lcp'].forEach(name => {
- if (!_measurements[name] || !transactionStartTime || timeOrigin >= transactionStartTime) {
- return;
- }
-
-
-
- const oldValue = _measurements[name].value;
- const measurementTimestamp = timeOrigin + msToSec(oldValue);
-
- const normalizedValue = Math.abs((measurementTimestamp - transactionStartTime) * 1000);
- const delta = normalizedValue - oldValue;
- debugBuild.DEBUG_BUILD && utils.logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`);
- _measurements[name].value = normalizedValue;
- });
- const fidMark = _measurements['mark.fid'];
- if (fidMark && _measurements['fid']) {
-
- utils$1._startChild(transaction, {
- description: 'first input delay',
- endTimestamp: fidMark.value + msToSec(_measurements['fid'].value),
- op: 'ui.action',
- origin: 'auto.ui.browser.metrics',
- startTimestamp: fidMark.value,
- });
-
- delete _measurements['mark.fid'];
- }
-
-
- if (!('fcp' in _measurements)) {
- delete _measurements.cls;
- }
- Object.keys(_measurements).forEach(measurementName => {
- core.setMeasurement(measurementName, _measurements[measurementName].value, _measurements[measurementName].unit);
- });
- _tagMetricInfo(transaction);
- }
- _lcpEntry = undefined;
- _clsEntry = undefined;
- _measurements = {};
- }
- function _addMeasureSpans(
- transaction,
-
- entry,
- startTime,
- duration,
- timeOrigin,
- ) {
- const measureStartTimestamp = timeOrigin + startTime;
- const measureEndTimestamp = measureStartTimestamp + duration;
- utils$1._startChild(transaction, {
- description: entry.name ,
- endTimestamp: measureEndTimestamp,
- op: entry.entryType ,
- origin: 'auto.resource.browser.metrics',
- startTimestamp: measureStartTimestamp,
- });
- return measureStartTimestamp;
- }
- function _addNavigationSpans(transaction, entry, timeOrigin) {
- ['unloadEvent', 'redirect', 'domContentLoadedEvent', 'loadEvent', 'connect'].forEach(event => {
- _addPerformanceNavigationTiming(transaction, entry, event, timeOrigin);
- });
- _addPerformanceNavigationTiming(transaction, entry, 'secureConnection', timeOrigin, 'TLS/SSL', 'connectEnd');
- _addPerformanceNavigationTiming(transaction, entry, 'fetch', timeOrigin, 'cache', 'domainLookupStart');
- _addPerformanceNavigationTiming(transaction, entry, 'domainLookup', timeOrigin, 'DNS');
- _addRequest(transaction, entry, timeOrigin);
- }
- function _addPerformanceNavigationTiming(
- transaction,
-
- entry,
- event,
- timeOrigin,
- description,
- eventEnd,
- ) {
- const end = eventEnd ? (entry[eventEnd] ) : (entry[`${event}End`] );
- const start = entry[`${event}Start`] ;
- if (!start || !end) {
- return;
- }
- utils$1._startChild(transaction, {
- op: 'browser',
- origin: 'auto.browser.browser.metrics',
- description: description || event,
- startTimestamp: timeOrigin + msToSec(start),
- endTimestamp: timeOrigin + msToSec(end),
- });
- }
- function _addRequest(transaction, entry, timeOrigin) {
- if (entry.responseEnd) {
-
-
-
-
- utils$1._startChild(transaction, {
- op: 'browser',
- origin: 'auto.browser.browser.metrics',
- description: 'request',
- startTimestamp: timeOrigin + msToSec(entry.requestStart ),
- endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
- });
- utils$1._startChild(transaction, {
- op: 'browser',
- origin: 'auto.browser.browser.metrics',
- description: 'response',
- startTimestamp: timeOrigin + msToSec(entry.responseStart ),
- endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
- });
- }
- }
- function _addResourceSpans(
- transaction,
- entry,
- resourceUrl,
- startTime,
- duration,
- timeOrigin,
- ) {
-
-
- if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {
- return;
- }
- const parsedUrl = utils.parseUrl(resourceUrl);
-
- const data = {};
- setResourceEntrySizeData(data, entry, 'transferSize', 'http.response_transfer_size');
- setResourceEntrySizeData(data, entry, 'encodedBodySize', 'http.response_content_length');
- setResourceEntrySizeData(data, entry, 'decodedBodySize', 'http.decoded_response_content_length');
- if ('renderBlockingStatus' in entry) {
- data['resource.render_blocking_status'] = entry.renderBlockingStatus;
- }
- if (parsedUrl.protocol) {
- data['url.scheme'] = parsedUrl.protocol.split(':').pop();
- }
- if (parsedUrl.host) {
- data['server.address'] = parsedUrl.host;
- }
- data['url.same_origin'] = resourceUrl.includes(types.WINDOW.location.origin);
- const startTimestamp = timeOrigin + startTime;
- const endTimestamp = startTimestamp + duration;
- utils$1._startChild(transaction, {
- description: resourceUrl.replace(types.WINDOW.location.origin, ''),
- endTimestamp,
- op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other',
- origin: 'auto.resource.browser.metrics',
- startTimestamp,
- data,
- });
- }
- function _trackNavigator(transaction) {
- const navigator = types.WINDOW.navigator ;
- if (!navigator) {
- return;
- }
-
- const connection = navigator.connection;
- if (connection) {
- if (connection.effectiveType) {
-
-
- transaction.setTag('effectiveConnectionType', connection.effectiveType);
- }
- if (connection.type) {
-
-
- transaction.setTag('connectionType', connection.type);
- }
- if (utils$1.isMeasurementValue(connection.rtt)) {
- _measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' };
- }
- }
- if (utils$1.isMeasurementValue(navigator.deviceMemory)) {
-
-
- transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`);
- }
- if (utils$1.isMeasurementValue(navigator.hardwareConcurrency)) {
-
-
- transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency));
- }
- }
- function _tagMetricInfo(transaction) {
- if (_lcpEntry) {
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding LCP Data');
-
- if (_lcpEntry.element) {
-
-
- transaction.setTag('lcp.element', utils.htmlTreeAsString(_lcpEntry.element));
- }
- if (_lcpEntry.id) {
-
-
- transaction.setTag('lcp.id', _lcpEntry.id);
- }
- if (_lcpEntry.url) {
-
-
-
- transaction.setTag('lcp.url', _lcpEntry.url.trim().slice(0, 200));
- }
-
-
- transaction.setTag('lcp.size', _lcpEntry.size);
- }
-
- if (_clsEntry && _clsEntry.sources) {
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding CLS Data');
- _clsEntry.sources.forEach((source, index) =>
-
-
- transaction.setTag(`cls.source.${index + 1}`, utils.htmlTreeAsString(source.node)),
- );
- }
- }
- function setResourceEntrySizeData(
- data,
- entry,
- key,
- dataKey,
- ) {
- const entryVal = entry[key];
- if (entryVal != null && entryVal < MAX_INT_AS_BYTES) {
- data[dataKey] = entryVal;
- }
- }
- function _addTtfbToMeasurements(
- _measurements,
- responseStartTimestamp,
- requestStartTimestamp,
- transactionStartTime,
- ) {
-
-
- if (typeof responseStartTimestamp === 'number' && transactionStartTime) {
- debugBuild.DEBUG_BUILD && utils.logger.log('[Measurements] Adding TTFB');
- _measurements['ttfb'] = {
-
-
-
-
-
-
-
- value: Math.max(responseStartTimestamp - transactionStartTime, 0) * 1000,
- unit: 'millisecond',
- };
- if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) {
-
-
- _measurements['ttfb.requestTime'] = {
- value: (responseStartTimestamp - requestStartTimestamp) * 1000,
- unit: 'millisecond',
- };
- }
- }
- }
- exports._addMeasureSpans = _addMeasureSpans;
- exports._addResourceSpans = _addResourceSpans;
- exports._addTtfbToMeasurements = _addTtfbToMeasurements;
- exports.addPerformanceEntries = addPerformanceEntries;
- exports.startTrackingInteractions = startTrackingInteractions;
- exports.startTrackingLongTasks = startTrackingLongTasks;
- exports.startTrackingWebVitals = startTrackingWebVitals;
|