mirror of
https://github.com/rxliuli/apps.apple.com.git
synced 2025-11-10 00:10:34 +00:00
init commit
This commit is contained in:
484
shared/logger/node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js
generated
vendored
Normal file
484
shared/logger/node_modules/@sentry-internal/tracing/esm/browser/metrics/index.js
generated
vendored
Normal file
@@ -0,0 +1,484 @@
|
||||
import { getActiveTransaction } from '@sentry/core';
|
||||
import { browserPerformanceTimeOrigin, logger, htmlTreeAsString } from '@sentry/utils';
|
||||
import { WINDOW } from '../types.js';
|
||||
import { onCLS } from '../web-vitals/getCLS.js';
|
||||
import { onFID } from '../web-vitals/getFID.js';
|
||||
import { onLCP } from '../web-vitals/getLCP.js';
|
||||
import { getVisibilityWatcher } from '../web-vitals/lib/getVisibilityWatcher.js';
|
||||
import { observe } from '../web-vitals/lib/observe.js';
|
||||
import { _startChild, isMeasurementValue } from './utils.js';
|
||||
|
||||
/**
|
||||
* Converts from milliseconds to seconds
|
||||
* @param time time in ms
|
||||
*/
|
||||
function msToSec(time) {
|
||||
return time / 1000;
|
||||
}
|
||||
|
||||
function getBrowserPerformanceAPI() {
|
||||
// @ts-ignore we want to make sure all of these are available, even if TS is sure they are
|
||||
return WINDOW && WINDOW.addEventListener && WINDOW.performance;
|
||||
}
|
||||
|
||||
let _performanceCursor = 0;
|
||||
|
||||
let _measurements = {};
|
||||
let _lcpEntry;
|
||||
let _clsEntry;
|
||||
|
||||
/**
|
||||
* Start tracking web vitals
|
||||
*
|
||||
* @returns A function that forces web vitals collection
|
||||
*/
|
||||
function startTrackingWebVitals() {
|
||||
const performance = getBrowserPerformanceAPI();
|
||||
if (performance && browserPerformanceTimeOrigin) {
|
||||
// @ts-ignore we want to make sure all of these are available, even if TS is sure they are
|
||||
if (performance.mark) {
|
||||
WINDOW.performance.mark('sentry-tracing-init');
|
||||
}
|
||||
_trackFID();
|
||||
const clsCallback = _trackCLS();
|
||||
const lcpCallback = _trackLCP();
|
||||
|
||||
return () => {
|
||||
if (clsCallback) {
|
||||
clsCallback();
|
||||
}
|
||||
if (lcpCallback) {
|
||||
lcpCallback();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking long tasks.
|
||||
*/
|
||||
function startTrackingLongTasks() {
|
||||
const entryHandler = (entries) => {
|
||||
for (const entry of entries) {
|
||||
const transaction = getActiveTransaction() ;
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
const startTime = msToSec((browserPerformanceTimeOrigin ) + entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
transaction.startChild({
|
||||
description: 'Main UI thread blocked',
|
||||
op: 'ui.long-task',
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: startTime + duration,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
observe('longtask', entryHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking interaction events.
|
||||
*/
|
||||
function startTrackingInteractions() {
|
||||
const entryHandler = (entries) => {
|
||||
for (const entry of entries) {
|
||||
const transaction = getActiveTransaction() ;
|
||||
if (!transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.name === 'click') {
|
||||
const startTime = msToSec((browserPerformanceTimeOrigin ) + entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
transaction.startChild({
|
||||
description: htmlTreeAsString(entry.target),
|
||||
op: `ui.interaction.${entry.name}`,
|
||||
startTimestamp: startTime,
|
||||
endTimestamp: startTime + duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
observe('event', entryHandler, { durationThreshold: 0 });
|
||||
}
|
||||
|
||||
/** Starts tracking the Cumulative Layout Shift on the current page. */
|
||||
function _trackCLS() {
|
||||
// See:
|
||||
// https://web.dev/evolving-cls/
|
||||
// https://web.dev/cls-web-tooling/
|
||||
return onCLS(metric => {
|
||||
const entry = metric.entries.pop();
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding CLS');
|
||||
_measurements['cls'] = { value: metric.value, unit: '' };
|
||||
_clsEntry = entry ;
|
||||
});
|
||||
}
|
||||
|
||||
/** Starts tracking the Largest Contentful Paint on the current page. */
|
||||
function _trackLCP() {
|
||||
return onLCP(metric => {
|
||||
const entry = metric.entries.pop();
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding LCP');
|
||||
_measurements['lcp'] = { value: metric.value, unit: 'millisecond' };
|
||||
_lcpEntry = entry ;
|
||||
});
|
||||
}
|
||||
|
||||
/** Starts tracking the First Input Delay on the current page. */
|
||||
function _trackFID() {
|
||||
onFID(metric => {
|
||||
const entry = metric.entries.pop();
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeOrigin = msToSec(browserPerformanceTimeOrigin );
|
||||
const startTime = msToSec(entry.startTime);
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding FID');
|
||||
_measurements['fid'] = { value: metric.value, unit: 'millisecond' };
|
||||
_measurements['mark.fid'] = { value: timeOrigin + startTime, unit: 'second' };
|
||||
});
|
||||
}
|
||||
|
||||
/** Add performance related spans to a transaction */
|
||||
function addPerformanceEntries(transaction) {
|
||||
const performance = getBrowserPerformanceAPI();
|
||||
if (!performance || !WINDOW.performance.getEntries || !browserPerformanceTimeOrigin) {
|
||||
// Gatekeeper if performance API not available
|
||||
return;
|
||||
}
|
||||
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Tracing] Adding & adjusting spans using Performance API');
|
||||
const timeOrigin = msToSec(browserPerformanceTimeOrigin);
|
||||
|
||||
const performanceEntries = performance.getEntries();
|
||||
|
||||
let responseStartTimestamp;
|
||||
let requestStartTimestamp;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
performanceEntries.slice(_performanceCursor).forEach((entry) => {
|
||||
const startTime = msToSec(entry.startTime);
|
||||
const duration = msToSec(entry.duration);
|
||||
|
||||
if (transaction.op === 'navigation' && timeOrigin + startTime < transaction.startTimestamp) {
|
||||
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);
|
||||
|
||||
// capture web vitals
|
||||
const firstHidden = getVisibilityWatcher();
|
||||
// Only report if the page wasn't hidden prior to the web vital.
|
||||
const shouldRecord = entry.startTime < firstHidden.firstHiddenTime;
|
||||
|
||||
if (entry.name === 'first-paint' && shouldRecord) {
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding FP');
|
||||
_measurements['fp'] = { value: entry.startTime, unit: 'millisecond' };
|
||||
}
|
||||
if (entry.name === 'first-contentful-paint' && shouldRecord) {
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding FCP');
|
||||
_measurements['fcp'] = { value: entry.startTime, unit: 'millisecond' };
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'resource': {
|
||||
const resourceName = (entry.name ).replace(WINDOW.location.origin, '');
|
||||
_addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin);
|
||||
break;
|
||||
}
|
||||
// Ignore other entry types.
|
||||
}
|
||||
});
|
||||
|
||||
_performanceCursor = Math.max(performanceEntries.length - 1, 0);
|
||||
|
||||
_trackNavigator(transaction);
|
||||
|
||||
// Measurements are only available for pageload transactions
|
||||
if (transaction.op === 'pageload') {
|
||||
// Generate TTFB (Time to First Byte), which measured as the time between the beginning of the transaction and the
|
||||
// start of the response in milliseconds
|
||||
if (typeof responseStartTimestamp === 'number') {
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding TTFB');
|
||||
_measurements['ttfb'] = {
|
||||
value: (responseStartTimestamp - transaction.startTimestamp) * 1000,
|
||||
unit: 'millisecond',
|
||||
};
|
||||
|
||||
if (typeof requestStartTimestamp === 'number' && requestStartTimestamp <= responseStartTimestamp) {
|
||||
// Capture the time spent making the request and receiving the first byte of the response.
|
||||
// This is the time between the start of the request and the start of the response in milliseconds.
|
||||
_measurements['ttfb.requestTime'] = {
|
||||
value: (responseStartTimestamp - requestStartTimestamp) * 1000,
|
||||
unit: 'millisecond',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
['fcp', 'fp', 'lcp'].forEach(name => {
|
||||
if (!_measurements[name] || timeOrigin >= transaction.startTimestamp) {
|
||||
return;
|
||||
}
|
||||
// The web vitals, fcp, fp, lcp, and ttfb, all measure relative to timeOrigin.
|
||||
// Unfortunately, timeOrigin is not captured within the transaction span data, so these web vitals will need
|
||||
// to be adjusted to be relative to transaction.startTimestamp.
|
||||
const oldValue = _measurements[name].value;
|
||||
const measurementTimestamp = timeOrigin + msToSec(oldValue);
|
||||
|
||||
// normalizedValue should be in milliseconds
|
||||
const normalizedValue = Math.abs((measurementTimestamp - transaction.startTimestamp) * 1000);
|
||||
const delta = normalizedValue - oldValue;
|
||||
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
|
||||
logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`);
|
||||
_measurements[name].value = normalizedValue;
|
||||
});
|
||||
|
||||
const fidMark = _measurements['mark.fid'];
|
||||
if (fidMark && _measurements['fid']) {
|
||||
// create span for FID
|
||||
_startChild(transaction, {
|
||||
description: 'first input delay',
|
||||
endTimestamp: fidMark.value + msToSec(_measurements['fid'].value),
|
||||
op: 'ui.action',
|
||||
startTimestamp: fidMark.value,
|
||||
});
|
||||
|
||||
// Delete mark.fid as we don't want it to be part of final payload
|
||||
delete _measurements['mark.fid'];
|
||||
}
|
||||
|
||||
// If FCP is not recorded we should not record the cls value
|
||||
// according to the new definition of CLS.
|
||||
if (!('fcp' in _measurements)) {
|
||||
delete _measurements.cls;
|
||||
}
|
||||
|
||||
Object.keys(_measurements).forEach(measurementName => {
|
||||
transaction.setMeasurement(
|
||||
measurementName,
|
||||
_measurements[measurementName].value,
|
||||
_measurements[measurementName].unit,
|
||||
);
|
||||
});
|
||||
|
||||
_tagMetricInfo(transaction);
|
||||
}
|
||||
|
||||
_lcpEntry = undefined;
|
||||
_clsEntry = undefined;
|
||||
_measurements = {};
|
||||
}
|
||||
|
||||
/** Create measure related spans */
|
||||
function _addMeasureSpans(
|
||||
transaction,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
entry,
|
||||
startTime,
|
||||
duration,
|
||||
timeOrigin,
|
||||
) {
|
||||
const measureStartTimestamp = timeOrigin + startTime;
|
||||
const measureEndTimestamp = measureStartTimestamp + duration;
|
||||
|
||||
_startChild(transaction, {
|
||||
description: entry.name ,
|
||||
endTimestamp: measureEndTimestamp,
|
||||
op: entry.entryType ,
|
||||
startTimestamp: measureStartTimestamp,
|
||||
});
|
||||
|
||||
return measureStartTimestamp;
|
||||
}
|
||||
|
||||
/** Instrument navigation entries */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
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);
|
||||
}
|
||||
|
||||
/** Create performance navigation related spans */
|
||||
function _addPerformanceNavigationTiming(
|
||||
transaction,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
entry,
|
||||
event,
|
||||
timeOrigin,
|
||||
description,
|
||||
eventEnd,
|
||||
) {
|
||||
const end = eventEnd ? (entry[eventEnd] ) : (entry[`${event}End`] );
|
||||
const start = entry[`${event}Start`] ;
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
description: description || event,
|
||||
startTimestamp: timeOrigin + msToSec(start),
|
||||
endTimestamp: timeOrigin + msToSec(end),
|
||||
});
|
||||
}
|
||||
|
||||
/** Create request and response related spans */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function _addRequest(transaction, entry, timeOrigin) {
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
description: 'request',
|
||||
startTimestamp: timeOrigin + msToSec(entry.requestStart ),
|
||||
endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
|
||||
});
|
||||
|
||||
_startChild(transaction, {
|
||||
op: 'browser',
|
||||
description: 'response',
|
||||
startTimestamp: timeOrigin + msToSec(entry.responseStart ),
|
||||
endTimestamp: timeOrigin + msToSec(entry.responseEnd ),
|
||||
});
|
||||
}
|
||||
|
||||
/** Create resource-related spans */
|
||||
function _addResourceSpans(
|
||||
transaction,
|
||||
entry,
|
||||
resourceName,
|
||||
startTime,
|
||||
duration,
|
||||
timeOrigin,
|
||||
) {
|
||||
// we already instrument based on fetch and xhr, so we don't need to
|
||||
// duplicate spans here.
|
||||
if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const data = {};
|
||||
if ('transferSize' in entry) {
|
||||
data['http.response_transfer_size'] = entry.transferSize;
|
||||
}
|
||||
if ('encodedBodySize' in entry) {
|
||||
data['http.response_content_length'] = entry.encodedBodySize;
|
||||
}
|
||||
if ('decodedBodySize' in entry) {
|
||||
data['http.decoded_response_content_length'] = entry.decodedBodySize;
|
||||
}
|
||||
if ('renderBlockingStatus' in entry) {
|
||||
data['resource.render_blocking_status'] = entry.renderBlockingStatus;
|
||||
}
|
||||
|
||||
const startTimestamp = timeOrigin + startTime;
|
||||
const endTimestamp = startTimestamp + duration;
|
||||
|
||||
_startChild(transaction, {
|
||||
description: resourceName,
|
||||
endTimestamp,
|
||||
op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other',
|
||||
startTimestamp,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the information of the user agent.
|
||||
*/
|
||||
function _trackNavigator(transaction) {
|
||||
const navigator = WINDOW.navigator ;
|
||||
if (!navigator) {
|
||||
return;
|
||||
}
|
||||
|
||||
// track network connectivity
|
||||
const connection = navigator.connection;
|
||||
if (connection) {
|
||||
if (connection.effectiveType) {
|
||||
transaction.setTag('effectiveConnectionType', connection.effectiveType);
|
||||
}
|
||||
|
||||
if (connection.type) {
|
||||
transaction.setTag('connectionType', connection.type);
|
||||
}
|
||||
|
||||
if (isMeasurementValue(connection.rtt)) {
|
||||
_measurements['connection.rtt'] = { value: connection.rtt, unit: 'millisecond' };
|
||||
}
|
||||
}
|
||||
|
||||
if (isMeasurementValue(navigator.deviceMemory)) {
|
||||
transaction.setTag('deviceMemory', `${navigator.deviceMemory} GB`);
|
||||
}
|
||||
|
||||
if (isMeasurementValue(navigator.hardwareConcurrency)) {
|
||||
transaction.setTag('hardwareConcurrency', String(navigator.hardwareConcurrency));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add LCP / CLS data to transaction to allow debugging */
|
||||
function _tagMetricInfo(transaction) {
|
||||
if (_lcpEntry) {
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding LCP Data');
|
||||
|
||||
// Capture Properties of the LCP element that contributes to the LCP.
|
||||
|
||||
if (_lcpEntry.element) {
|
||||
transaction.setTag('lcp.element', htmlTreeAsString(_lcpEntry.element));
|
||||
}
|
||||
|
||||
if (_lcpEntry.id) {
|
||||
transaction.setTag('lcp.id', _lcpEntry.id);
|
||||
}
|
||||
|
||||
if (_lcpEntry.url) {
|
||||
// Trim URL to the first 200 characters.
|
||||
transaction.setTag('lcp.url', _lcpEntry.url.trim().slice(0, 200));
|
||||
}
|
||||
|
||||
transaction.setTag('lcp.size', _lcpEntry.size);
|
||||
}
|
||||
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/LayoutShift
|
||||
if (_clsEntry && _clsEntry.sources) {
|
||||
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log('[Measurements] Adding CLS Data');
|
||||
_clsEntry.sources.forEach((source, index) =>
|
||||
transaction.setTag(`cls.source.${index + 1}`, htmlTreeAsString(source.node)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { _addMeasureSpans, _addResourceSpans, addPerformanceEntries, startTrackingInteractions, startTrackingLongTasks, startTrackingWebVitals };
|
||||
//# sourceMappingURL=index.js.map
|
||||
25
shared/logger/node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js
generated
vendored
Normal file
25
shared/logger/node_modules/@sentry-internal/tracing/esm/browser/metrics/utils.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Checks if a given value is a valid measurement value.
|
||||
*/
|
||||
function isMeasurementValue(value) {
|
||||
return typeof value === 'number' && isFinite(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to start child on transactions. This function will make sure that the transaction will
|
||||
* use the start timestamp of the created child span if it is earlier than the transactions actual
|
||||
* start timestamp.
|
||||
*/
|
||||
function _startChild(transaction, { startTimestamp, ...ctx }) {
|
||||
if (startTimestamp && transaction.startTimestamp > startTimestamp) {
|
||||
transaction.startTimestamp = startTimestamp;
|
||||
}
|
||||
|
||||
return transaction.startChild({
|
||||
startTimestamp,
|
||||
...ctx,
|
||||
});
|
||||
}
|
||||
|
||||
export { _startChild, isMeasurementValue };
|
||||
//# sourceMappingURL=utils.js.map
|
||||
Reference in New Issue
Block a user