@sentry/react-native
Version:
Official Sentry SDK for react-native
197 lines • 11.3 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { debug } from '@sentry/core';
import { NATIVE } from '../../wrapper';
import { UI_LOAD_FULL_DISPLAY, UI_LOAD_INITIAL_DISPLAY } from '../ops';
import { SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from '../origin';
import { getReactNavigationIntegration } from '../reactnavigation';
import { SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN } from '../semanticAttributes';
import { SPAN_THREAD_NAME, SPAN_THREAD_NAME_JAVASCRIPT } from '../span';
import { getTimeToInitialDisplayFallback } from '../timeToDisplayFallback';
import { createSpanJSON } from '../utils';
export const INTEGRATION_NAME = 'TimeToDisplay';
const TIME_TO_DISPLAY_TIMEOUT_MS = 30000;
const isDeadlineExceeded = (durationMs) => durationMs > TIME_TO_DISPLAY_TIMEOUT_MS;
export const timeToDisplayIntegration = () => {
let enableTimeToInitialDisplayForPreloadedRoutes = false;
return {
name: INTEGRATION_NAME,
afterAllSetup(client) {
var _a, _b;
enableTimeToInitialDisplayForPreloadedRoutes =
(_b = (_a = getReactNavigationIntegration(client)) === null || _a === void 0 ? void 0 : _a.options.enableTimeToInitialDisplayForPreloadedRoutes) !== null && _b !== void 0 ? _b : false;
},
processEvent: (event) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b, _c, _d, _e;
if (event.type !== 'transaction') {
// TimeToDisplay data is only relevant for transactions
return event;
}
const rootSpanId = (_b = (_a = event.contexts) === null || _a === void 0 ? void 0 : _a.trace) === null || _b === void 0 ? void 0 : _b.span_id;
if (!rootSpanId) {
debug.warn(`[${INTEGRATION_NAME}] No root span id found in transaction.`);
return event;
}
const transactionStartTimestampSeconds = event.start_timestamp;
if (!transactionStartTimestampSeconds) {
// This should never happen
debug.warn(`[${INTEGRATION_NAME}] No transaction start timestamp found in transaction.`);
return event;
}
event.spans = event.spans || [];
event.measurements = event.measurements || {};
const ttidSpan = yield addTimeToInitialDisplay({
event,
rootSpanId,
transactionStartTimestampSeconds,
enableTimeToInitialDisplayForPreloadedRoutes,
});
const ttfdSpan = yield addTimeToFullDisplay({ event, rootSpanId, transactionStartTimestampSeconds, ttidSpan });
if ((ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.start_timestamp) && (ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.timestamp)) {
event.measurements['time_to_initial_display'] = {
value: (ttidSpan.timestamp - ttidSpan.start_timestamp) * 1000,
unit: 'millisecond',
};
}
if ((ttfdSpan === null || ttfdSpan === void 0 ? void 0 : ttfdSpan.start_timestamp) && (ttfdSpan === null || ttfdSpan === void 0 ? void 0 : ttfdSpan.timestamp)) {
const durationMs = (ttfdSpan.timestamp - ttfdSpan.start_timestamp) * 1000;
if (isDeadlineExceeded(durationMs)) {
if (event.measurements['time_to_initial_display']) {
event.measurements['time_to_full_display'] = event.measurements['time_to_initial_display'];
}
}
else {
event.measurements['time_to_full_display'] = {
value: durationMs,
unit: 'millisecond',
};
}
}
const newTransactionEndTimestampSeconds = Math.max((_c = ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.timestamp) !== null && _c !== void 0 ? _c : -1, (_d = ttfdSpan === null || ttfdSpan === void 0 ? void 0 : ttfdSpan.timestamp) !== null && _d !== void 0 ? _d : -1, (_e = event.timestamp) !== null && _e !== void 0 ? _e : -1);
if (newTransactionEndTimestampSeconds !== -1) {
event.timestamp = newTransactionEndTimestampSeconds;
}
return event;
}),
};
};
function addTimeToInitialDisplay({ event, rootSpanId, transactionStartTimestampSeconds, enableTimeToInitialDisplayForPreloadedRoutes, }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const ttidEndTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttid-${rootSpanId}`);
event.spans = event.spans || [];
let ttidSpan = (_a = event.spans) === null || _a === void 0 ? void 0 : _a.find(span => span.op === UI_LOAD_INITIAL_DISPLAY);
if (ttidSpan && (ttidSpan.status === undefined || ttidSpan.status === 'ok') && !ttidEndTimestampSeconds) {
debug.log(`[${INTEGRATION_NAME}] Ttid span already exists and is ok.`, ttidSpan);
return ttidSpan;
}
if (!ttidEndTimestampSeconds) {
debug.log(`[${INTEGRATION_NAME}] No manual ttid end timestamp found for span ${rootSpanId}.`);
return addAutomaticTimeToInitialDisplay({
event,
rootSpanId,
transactionStartTimestampSeconds,
enableTimeToInitialDisplayForPreloadedRoutes,
});
}
if ((ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.status) && ttidSpan.status !== 'ok') {
ttidSpan.status = 'ok';
ttidSpan.timestamp = ttidEndTimestampSeconds;
debug.log(`[${INTEGRATION_NAME}] Updated existing ttid span.`, ttidSpan);
return ttidSpan;
}
ttidSpan = createSpanJSON({
op: UI_LOAD_INITIAL_DISPLAY,
description: 'Time To Initial Display',
start_timestamp: transactionStartTimestampSeconds,
timestamp: ttidEndTimestampSeconds,
origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,
parent_span_id: rootSpanId,
data: {
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
},
});
debug.log(`[${INTEGRATION_NAME}] Added ttid span to transaction.`, ttidSpan);
event.spans.push(ttidSpan);
return ttidSpan;
});
}
function addAutomaticTimeToInitialDisplay({ event, rootSpanId, transactionStartTimestampSeconds, enableTimeToInitialDisplayForPreloadedRoutes, }) {
var _a, _b, _c, _d, _e, _f;
return __awaiter(this, void 0, void 0, function* () {
const ttidNativeTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttid-navigation-${rootSpanId}`);
const ttidFallbackTimestampSeconds = yield getTimeToInitialDisplayFallback(rootSpanId);
const hasBeenSeen = (_c = (_b = (_a = event.contexts) === null || _a === void 0 ? void 0 : _a.trace) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c[SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN];
if (hasBeenSeen && !enableTimeToInitialDisplayForPreloadedRoutes) {
debug.log(`[${INTEGRATION_NAME}] Route has been seen and time to initial display is disabled for preloaded routes.`);
return undefined;
}
const ttidTimestampSeconds = ttidNativeTimestampSeconds !== null && ttidNativeTimestampSeconds !== void 0 ? ttidNativeTimestampSeconds : ttidFallbackTimestampSeconds;
if (!ttidTimestampSeconds) {
debug.log(`[${INTEGRATION_NAME}] No automatic ttid end timestamp found for span ${rootSpanId}.`);
return undefined;
}
const viewNames = (_e = (_d = event.contexts) === null || _d === void 0 ? void 0 : _d.app) === null || _e === void 0 ? void 0 : _e.view_names;
const screenName = Array.isArray(viewNames) ? viewNames[0] : viewNames;
const ttidSpan = createSpanJSON({
op: UI_LOAD_INITIAL_DISPLAY,
description: screenName ? `${screenName} initial display` : 'Time To Initial Display',
start_timestamp: transactionStartTimestampSeconds,
timestamp: ttidTimestampSeconds,
origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY,
parent_span_id: rootSpanId,
data: {
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
},
});
event.spans = (_f = event.spans) !== null && _f !== void 0 ? _f : [];
event.spans.push(ttidSpan);
return ttidSpan;
});
}
function addTimeToFullDisplay({ event, rootSpanId, transactionStartTimestampSeconds, ttidSpan, }) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const ttfdEndTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttfd-${rootSpanId}`);
if (!ttidSpan || !ttfdEndTimestampSeconds) {
return undefined;
}
event.spans = event.spans || [];
let ttfdSpan = (_a = event.spans) === null || _a === void 0 ? void 0 : _a.find(span => span.op === UI_LOAD_FULL_DISPLAY);
let ttfdAdjustedEndTimestampSeconds = ttfdEndTimestampSeconds;
const ttfdIsBeforeTtid = ttidSpan.timestamp && ttfdEndTimestampSeconds < ttidSpan.timestamp;
if (ttfdIsBeforeTtid && ttidSpan.timestamp) {
ttfdAdjustedEndTimestampSeconds = ttidSpan.timestamp;
}
const durationMs = (ttfdAdjustedEndTimestampSeconds - transactionStartTimestampSeconds) * 1000;
if ((ttfdSpan === null || ttfdSpan === void 0 ? void 0 : ttfdSpan.status) && ttfdSpan.status !== 'ok') {
ttfdSpan.status = 'ok';
ttfdSpan.timestamp = ttfdAdjustedEndTimestampSeconds;
debug.log(`[${INTEGRATION_NAME}] Updated existing ttfd span.`, ttfdSpan);
return ttfdSpan;
}
ttfdSpan = createSpanJSON({
status: isDeadlineExceeded(durationMs) ? 'deadline_exceeded' : 'ok',
op: UI_LOAD_FULL_DISPLAY,
description: 'Time To Full Display',
start_timestamp: transactionStartTimestampSeconds,
timestamp: ttfdAdjustedEndTimestampSeconds,
origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,
parent_span_id: rootSpanId,
data: {
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
},
});
debug.log(`[${INTEGRATION_NAME}] Added ttfd span to transaction.`, ttfdSpan);
event.spans.push(ttfdSpan);
return ttfdSpan;
});
}
//# sourceMappingURL=timeToDisplayIntegration.js.map