@sentry/react-native 6.10.0 → 6.11.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/RNSentry.podspec +1 -1
- package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +43 -21
- package/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java +82 -51
- package/android/src/main/java/io/sentry/react/RNSentryTimeToDisplay.java +37 -0
- package/android/src/main/java/io/sentry/react/RNSentryVersion.java +1 -1
- package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +10 -0
- package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +16 -6
- package/dist/js/NativeRNSentry.d.ts +2 -0
- package/dist/js/NativeRNSentry.d.ts.map +1 -1
- package/dist/js/NativeRNSentry.js.map +1 -1
- package/dist/js/index.d.ts +1 -1
- package/dist/js/index.d.ts.map +1 -1
- package/dist/js/index.js +1 -1
- package/dist/js/index.js.map +1 -1
- package/dist/js/integrations/appRegistry.d.ts +8 -0
- package/dist/js/integrations/appRegistry.d.ts.map +1 -0
- package/dist/js/integrations/appRegistry.js +43 -0
- package/dist/js/integrations/appRegistry.js.map +1 -0
- package/dist/js/integrations/default.d.ts.map +1 -1
- package/dist/js/integrations/default.js +5 -1
- package/dist/js/integrations/default.js.map +1 -1
- package/dist/js/integrations/exports.d.ts +2 -0
- package/dist/js/integrations/exports.d.ts.map +1 -1
- package/dist/js/integrations/exports.js +2 -0
- package/dist/js/integrations/exports.js.map +1 -1
- package/dist/js/profiling/integration.d.ts.map +1 -1
- package/dist/js/profiling/integration.js +2 -3
- package/dist/js/profiling/integration.js.map +1 -1
- package/dist/js/profiling/nativeTypes.d.ts +1 -1
- package/dist/js/profiling/nativeTypes.d.ts.map +1 -1
- package/dist/js/profiling/nativeTypes.js.map +1 -1
- package/dist/js/replay/CustomMask.d.ts.map +1 -1
- package/dist/js/replay/CustomMask.js +3 -2
- package/dist/js/replay/CustomMask.js.map +1 -1
- package/dist/js/tracing/integrations/appStart.d.ts.map +1 -1
- package/dist/js/tracing/integrations/appStart.js +38 -7
- package/dist/js/tracing/integrations/appStart.js.map +1 -1
- package/dist/js/tracing/integrations/timeToDisplayIntegration.d.ts +4 -0
- package/dist/js/tracing/integrations/timeToDisplayIntegration.d.ts.map +1 -0
- package/dist/js/tracing/integrations/timeToDisplayIntegration.js +193 -0
- package/dist/js/tracing/integrations/timeToDisplayIntegration.js.map +1 -0
- package/dist/js/tracing/ops.d.ts +2 -0
- package/dist/js/tracing/ops.d.ts.map +1 -1
- package/dist/js/tracing/ops.js +2 -0
- package/dist/js/tracing/ops.js.map +1 -1
- package/dist/js/tracing/reactnativeprofiler.d.ts.map +1 -1
- package/dist/js/tracing/reactnativeprofiler.js +12 -1
- package/dist/js/tracing/reactnativeprofiler.js.map +1 -1
- package/dist/js/tracing/reactnavigation.d.ts +20 -2
- package/dist/js/tracing/reactnavigation.d.ts.map +1 -1
- package/dist/js/tracing/reactnavigation.js +68 -40
- package/dist/js/tracing/reactnavigation.js.map +1 -1
- package/dist/js/tracing/semanticAttributes.d.ts +2 -0
- package/dist/js/tracing/semanticAttributes.d.ts.map +1 -1
- package/dist/js/tracing/semanticAttributes.js +2 -0
- package/dist/js/tracing/semanticAttributes.js.map +1 -1
- package/dist/js/tracing/timeToDisplayFallback.d.ts +3 -0
- package/dist/js/tracing/timeToDisplayFallback.d.ts.map +1 -0
- package/dist/js/tracing/timeToDisplayFallback.js +21 -0
- package/dist/js/tracing/timeToDisplayFallback.js.map +1 -0
- package/dist/js/tracing/timetodisplay.d.ts +29 -0
- package/dist/js/tracing/timetodisplay.d.ts.map +1 -1
- package/dist/js/tracing/timetodisplay.js +43 -20
- package/dist/js/tracing/timetodisplay.js.map +1 -1
- package/dist/js/tracing/timetodisplaynative.d.ts.map +1 -1
- package/dist/js/tracing/timetodisplaynative.js +2 -1
- package/dist/js/tracing/timetodisplaynative.js.map +1 -1
- package/dist/js/tracing/timetodisplaynative.types.d.ts +1 -3
- package/dist/js/tracing/timetodisplaynative.types.d.ts.map +1 -1
- package/dist/js/tracing/timetodisplaynative.types.js.map +1 -1
- package/dist/js/utils/rnlibraries.d.ts +1 -1
- package/dist/js/utils/rnlibraries.d.ts.map +1 -1
- package/dist/js/utils/rnlibraries.js +5 -3
- package/dist/js/utils/rnlibraries.js.map +1 -1
- package/dist/js/utils/rnlibrariesinterface.d.ts +1 -0
- package/dist/js/utils/rnlibrariesinterface.d.ts.map +1 -1
- package/dist/js/utils/rnlibrariesinterface.js.map +1 -1
- package/dist/js/vendor/react-native/index.d.ts +3 -0
- package/dist/js/vendor/react-native/index.d.ts.map +1 -1
- package/dist/js/vendor/react-native/index.js.map +1 -1
- package/dist/js/vendor/react-navigation/types.d.ts +27 -0
- package/dist/js/vendor/react-navigation/types.d.ts.map +1 -0
- package/dist/js/vendor/react-navigation/types.js +3 -0
- package/dist/js/vendor/react-navigation/types.js.map +1 -0
- package/dist/js/version.d.ts +1 -1
- package/dist/js/version.js +1 -1
- package/dist/js/version.js.map +1 -1
- package/dist/js/wrapper.d.ts +2 -0
- package/dist/js/wrapper.d.ts.map +1 -1
- package/dist/js/wrapper.js +24 -0
- package/dist/js/wrapper.js.map +1 -1
- package/ios/RNSentry.mm +15 -6
- package/ios/RNSentryFramesTrackerListener.h +7 -3
- package/ios/RNSentryOnDrawReporter.h +6 -2
- package/ios/RNSentryOnDrawReporter.m +47 -20
- package/ios/RNSentryTimeToDisplay.h +7 -0
- package/ios/RNSentryTimeToDisplay.m +69 -2
- package/ios/RNSentryVersion.m +1 -1
- package/package.json +4 -4
- package/scripts/sentry-xcode-debug-files.sh +1 -4
- package/scripts/sentry-xcode.sh +6 -4
- package/sentry.gradle +4 -2
- package/src/js/NativeRNSentry.ts +2 -0
- package/ts3.8/dist/js/NativeRNSentry.d.ts +2 -0
- package/ts3.8/dist/js/index.d.ts +1 -1
- package/ts3.8/dist/js/integrations/appRegistry.d.ts +8 -0
- package/ts3.8/dist/js/integrations/exports.d.ts +2 -0
- package/ts3.8/dist/js/profiling/nativeTypes.d.ts +1 -1
- package/ts3.8/dist/js/tracing/integrations/timeToDisplayIntegration.d.ts +4 -0
- package/ts3.8/dist/js/tracing/ops.d.ts +2 -0
- package/ts3.8/dist/js/tracing/reactnavigation.d.ts +20 -2
- package/ts3.8/dist/js/tracing/semanticAttributes.d.ts +2 -0
- package/ts3.8/dist/js/tracing/timeToDisplayFallback.d.ts +3 -0
- package/ts3.8/dist/js/tracing/timetodisplay.d.ts +29 -0
- package/ts3.8/dist/js/tracing/timetodisplaynative.types.d.ts +1 -3
- package/ts3.8/dist/js/utils/rnlibraries.d.ts +1 -1
- package/ts3.8/dist/js/utils/rnlibrariesinterface.d.ts +1 -0
- package/ts3.8/dist/js/vendor/react-native/index.d.ts +3 -0
- package/ts3.8/dist/js/vendor/react-navigation/types.d.ts +27 -0
- package/ts3.8/dist/js/version.d.ts +1 -1
- package/ts3.8/dist/js/wrapper.d.ts +2 -0
- package/dist/js/utils/sentryeventemitter.d.ts +0 -24
- package/dist/js/utils/sentryeventemitter.d.ts.map +0 -1
- package/dist/js/utils/sentryeventemitter.js +0 -82
- package/dist/js/utils/sentryeventemitter.js.map +0 -1
- package/dist/js/utils/sentryeventemitterfallback.d.ts +0 -19
- package/dist/js/utils/sentryeventemitterfallback.d.ts.map +0 -1
- package/dist/js/utils/sentryeventemitterfallback.js +0 -78
- package/dist/js/utils/sentryeventemitterfallback.js.map +0 -1
- package/ts3.8/dist/js/utils/sentryeventemitter.d.ts +0 -24
- package/ts3.8/dist/js/utils/sentryeventemitterfallback.d.ts +0 -19
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { logger } from '@sentry/core';
|
|
11
|
+
import { NATIVE } from '../../wrapper';
|
|
12
|
+
import { UI_LOAD_FULL_DISPLAY, UI_LOAD_INITIAL_DISPLAY } from '../ops';
|
|
13
|
+
import { SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from '../origin';
|
|
14
|
+
import { getReactNavigationIntegration } from '../reactnavigation';
|
|
15
|
+
import { SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN } from '../semanticAttributes';
|
|
16
|
+
import { SPAN_THREAD_NAME, SPAN_THREAD_NAME_JAVASCRIPT } from '../span';
|
|
17
|
+
import { getTimeToInitialDisplayFallback } from '../timeToDisplayFallback';
|
|
18
|
+
import { createSpanJSON } from '../utils';
|
|
19
|
+
export const INTEGRATION_NAME = 'TimeToDisplay';
|
|
20
|
+
const TIME_TO_DISPLAY_TIMEOUT_MS = 30000;
|
|
21
|
+
const isDeadlineExceeded = (durationMs) => durationMs > TIME_TO_DISPLAY_TIMEOUT_MS;
|
|
22
|
+
export const timeToDisplayIntegration = () => {
|
|
23
|
+
let enableTimeToInitialDisplayForPreloadedRoutes = false;
|
|
24
|
+
return {
|
|
25
|
+
name: INTEGRATION_NAME,
|
|
26
|
+
afterAllSetup(client) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
enableTimeToInitialDisplayForPreloadedRoutes =
|
|
29
|
+
(_b = (_a = getReactNavigationIntegration(client)) === null || _a === void 0 ? void 0 : _a.options.enableTimeToInitialDisplayForPreloadedRoutes) !== null && _b !== void 0 ? _b : false;
|
|
30
|
+
},
|
|
31
|
+
processEvent: (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
var _a, _b, _c;
|
|
33
|
+
if (event.type !== 'transaction') {
|
|
34
|
+
// TimeToDisplay data is only relevant for transactions
|
|
35
|
+
return event;
|
|
36
|
+
}
|
|
37
|
+
const rootSpanId = event.contexts.trace.span_id;
|
|
38
|
+
if (!rootSpanId) {
|
|
39
|
+
logger.warn(`[${INTEGRATION_NAME}] No root span id found in transaction.`);
|
|
40
|
+
return event;
|
|
41
|
+
}
|
|
42
|
+
const transactionStartTimestampSeconds = event.start_timestamp;
|
|
43
|
+
if (!transactionStartTimestampSeconds) {
|
|
44
|
+
// This should never happen
|
|
45
|
+
logger.warn(`[${INTEGRATION_NAME}] No transaction start timestamp found in transaction.`);
|
|
46
|
+
return event;
|
|
47
|
+
}
|
|
48
|
+
event.spans = event.spans || [];
|
|
49
|
+
event.measurements = event.measurements || {};
|
|
50
|
+
const ttidSpan = yield addTimeToInitialDisplay({
|
|
51
|
+
event,
|
|
52
|
+
rootSpanId,
|
|
53
|
+
transactionStartTimestampSeconds,
|
|
54
|
+
enableTimeToInitialDisplayForPreloadedRoutes,
|
|
55
|
+
});
|
|
56
|
+
const ttfdSpan = yield addTimeToFullDisplay({ event, rootSpanId, transactionStartTimestampSeconds, ttidSpan });
|
|
57
|
+
if (ttidSpan && ttidSpan.start_timestamp && ttidSpan.timestamp) {
|
|
58
|
+
event.measurements['time_to_initial_display'] = {
|
|
59
|
+
value: (ttidSpan.timestamp - ttidSpan.start_timestamp) * 1000,
|
|
60
|
+
unit: 'millisecond',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (ttfdSpan && ttfdSpan.start_timestamp && ttfdSpan.timestamp) {
|
|
64
|
+
const durationMs = (ttfdSpan.timestamp - ttfdSpan.start_timestamp) * 1000;
|
|
65
|
+
if (isDeadlineExceeded(durationMs)) {
|
|
66
|
+
event.measurements['time_to_full_display'] = event.measurements['time_to_initial_display'];
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
event.measurements['time_to_full_display'] = {
|
|
70
|
+
value: durationMs,
|
|
71
|
+
unit: 'millisecond',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const newTransactionEndTimestampSeconds = Math.max((_a = ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.timestamp) !== null && _a !== void 0 ? _a : -1, (_b = ttfdSpan === null || ttfdSpan === void 0 ? void 0 : ttfdSpan.timestamp) !== null && _b !== void 0 ? _b : -1, (_c = event.timestamp) !== null && _c !== void 0 ? _c : -1);
|
|
76
|
+
if (newTransactionEndTimestampSeconds !== -1) {
|
|
77
|
+
event.timestamp = newTransactionEndTimestampSeconds;
|
|
78
|
+
}
|
|
79
|
+
return event;
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
function addTimeToInitialDisplay({ event, rootSpanId, transactionStartTimestampSeconds, enableTimeToInitialDisplayForPreloadedRoutes, }) {
|
|
84
|
+
var _a;
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const ttidEndTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttid-${rootSpanId}`);
|
|
87
|
+
let ttidSpan = (_a = event.spans) === null || _a === void 0 ? void 0 : _a.find(span => span.op === UI_LOAD_INITIAL_DISPLAY);
|
|
88
|
+
if (ttidSpan && (ttidSpan.status === undefined || ttidSpan.status === 'ok') && !ttidEndTimestampSeconds) {
|
|
89
|
+
logger.debug(`[${INTEGRATION_NAME}] Ttid span already exists and is ok.`, ttidSpan);
|
|
90
|
+
return ttidSpan;
|
|
91
|
+
}
|
|
92
|
+
if (!ttidEndTimestampSeconds) {
|
|
93
|
+
logger.debug(`[${INTEGRATION_NAME}] No manual ttid end timestamp found for span ${rootSpanId}.`);
|
|
94
|
+
return addAutomaticTimeToInitialDisplay({
|
|
95
|
+
event,
|
|
96
|
+
rootSpanId,
|
|
97
|
+
transactionStartTimestampSeconds,
|
|
98
|
+
enableTimeToInitialDisplayForPreloadedRoutes,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (ttidSpan && ttidSpan.status && ttidSpan.status !== 'ok') {
|
|
102
|
+
ttidSpan.status = 'ok';
|
|
103
|
+
ttidSpan.timestamp = ttidEndTimestampSeconds;
|
|
104
|
+
logger.debug(`[${INTEGRATION_NAME}] Updated existing ttid span.`, ttidSpan);
|
|
105
|
+
return ttidSpan;
|
|
106
|
+
}
|
|
107
|
+
ttidSpan = createSpanJSON({
|
|
108
|
+
op: UI_LOAD_INITIAL_DISPLAY,
|
|
109
|
+
description: 'Time To Initial Display',
|
|
110
|
+
start_timestamp: transactionStartTimestampSeconds,
|
|
111
|
+
timestamp: ttidEndTimestampSeconds,
|
|
112
|
+
origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,
|
|
113
|
+
parent_span_id: rootSpanId,
|
|
114
|
+
data: {
|
|
115
|
+
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
logger.debug(`[${INTEGRATION_NAME}] Added ttid span to transaction.`, ttidSpan);
|
|
119
|
+
event.spans.push(ttidSpan);
|
|
120
|
+
return ttidSpan;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function addAutomaticTimeToInitialDisplay({ event, rootSpanId, transactionStartTimestampSeconds, enableTimeToInitialDisplayForPreloadedRoutes, }) {
|
|
124
|
+
var _a, _b, _c, _d, _e, _f;
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const ttidNativeTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttid-navigation-${rootSpanId}`);
|
|
127
|
+
const ttidFallbackTimestampSeconds = yield getTimeToInitialDisplayFallback(rootSpanId);
|
|
128
|
+
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];
|
|
129
|
+
if (hasBeenSeen && !enableTimeToInitialDisplayForPreloadedRoutes) {
|
|
130
|
+
logger.debug(`[${INTEGRATION_NAME}] Route has been seen and time to initial display is disabled for preloaded routes.`);
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const ttidTimestampSeconds = ttidNativeTimestampSeconds !== null && ttidNativeTimestampSeconds !== void 0 ? ttidNativeTimestampSeconds : ttidFallbackTimestampSeconds;
|
|
134
|
+
if (!ttidTimestampSeconds) {
|
|
135
|
+
logger.debug(`[${INTEGRATION_NAME}] No automatic ttid end timestamp found for span ${rootSpanId}.`);
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const viewNames = (_e = (_d = event.contexts) === null || _d === void 0 ? void 0 : _d.app) === null || _e === void 0 ? void 0 : _e.view_names;
|
|
139
|
+
const screenName = Array.isArray(viewNames) ? viewNames[0] : viewNames;
|
|
140
|
+
const ttidSpan = createSpanJSON({
|
|
141
|
+
op: UI_LOAD_INITIAL_DISPLAY,
|
|
142
|
+
description: screenName ? `${screenName} initial display` : 'Time To Initial Display',
|
|
143
|
+
start_timestamp: transactionStartTimestampSeconds,
|
|
144
|
+
timestamp: ttidTimestampSeconds,
|
|
145
|
+
origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY,
|
|
146
|
+
parent_span_id: rootSpanId,
|
|
147
|
+
data: {
|
|
148
|
+
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
event.spans = (_f = event.spans) !== null && _f !== void 0 ? _f : [];
|
|
152
|
+
event.spans.push(ttidSpan);
|
|
153
|
+
return ttidSpan;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function addTimeToFullDisplay({ event, rootSpanId, transactionStartTimestampSeconds, ttidSpan, }) {
|
|
157
|
+
var _a;
|
|
158
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
const ttfdEndTimestampSeconds = yield NATIVE.popTimeToDisplayFor(`ttfd-${rootSpanId}`);
|
|
160
|
+
if (!ttidSpan || !ttfdEndTimestampSeconds) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
let ttfdSpan = (_a = event.spans) === null || _a === void 0 ? void 0 : _a.find(span => span.op === UI_LOAD_FULL_DISPLAY);
|
|
164
|
+
let ttfdAdjustedEndTimestampSeconds = ttfdEndTimestampSeconds;
|
|
165
|
+
const ttfdIsBeforeTtid = (ttidSpan === null || ttidSpan === void 0 ? void 0 : ttidSpan.timestamp) && ttfdEndTimestampSeconds < ttidSpan.timestamp;
|
|
166
|
+
if (ttfdIsBeforeTtid) {
|
|
167
|
+
ttfdAdjustedEndTimestampSeconds = ttidSpan.timestamp;
|
|
168
|
+
}
|
|
169
|
+
const durationMs = (ttfdAdjustedEndTimestampSeconds - transactionStartTimestampSeconds) * 1000;
|
|
170
|
+
if (ttfdSpan && ttfdSpan.status && ttfdSpan.status !== 'ok') {
|
|
171
|
+
ttfdSpan.status = 'ok';
|
|
172
|
+
ttfdSpan.timestamp = ttfdAdjustedEndTimestampSeconds;
|
|
173
|
+
logger.debug(`[${INTEGRATION_NAME}] Updated existing ttfd span.`, ttfdSpan);
|
|
174
|
+
return ttfdSpan;
|
|
175
|
+
}
|
|
176
|
+
ttfdSpan = createSpanJSON({
|
|
177
|
+
status: isDeadlineExceeded(durationMs) ? 'deadline_exceeded' : 'ok',
|
|
178
|
+
op: UI_LOAD_FULL_DISPLAY,
|
|
179
|
+
description: 'Time To Full Display',
|
|
180
|
+
start_timestamp: transactionStartTimestampSeconds,
|
|
181
|
+
timestamp: ttfdAdjustedEndTimestampSeconds,
|
|
182
|
+
origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,
|
|
183
|
+
parent_span_id: rootSpanId,
|
|
184
|
+
data: {
|
|
185
|
+
[SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
logger.debug(`[${INTEGRATION_NAME}] Added ttfd span to transaction.`, ttfdSpan);
|
|
189
|
+
event.spans.push(ttfdSpan);
|
|
190
|
+
return ttfdSpan;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=timeToDisplayIntegration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeToDisplayIntegration.js","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/timeToDisplayIntegration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Event, Integration, SpanJSON } from '@sentry/core';\nimport { logger } from '@sentry/core';\n\nimport { NATIVE } from '../../wrapper';\nimport { UI_LOAD_FULL_DISPLAY, UI_LOAD_INITIAL_DISPLAY } from '../ops';\nimport { SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY, SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY } from '../origin';\nimport { getReactNavigationIntegration } from '../reactnavigation';\nimport { SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN } from '../semanticAttributes';\nimport { SPAN_THREAD_NAME, SPAN_THREAD_NAME_JAVASCRIPT } from '../span';\nimport { getTimeToInitialDisplayFallback } from '../timeToDisplayFallback';\nimport { createSpanJSON } from '../utils';\n\nexport const INTEGRATION_NAME = 'TimeToDisplay';\n\nconst TIME_TO_DISPLAY_TIMEOUT_MS = 30_000;\nconst isDeadlineExceeded = (durationMs: number): boolean => durationMs > TIME_TO_DISPLAY_TIMEOUT_MS;\n\nexport const timeToDisplayIntegration = (): Integration => {\n let enableTimeToInitialDisplayForPreloadedRoutes = false;\n\n return {\n name: INTEGRATION_NAME,\n afterAllSetup(client) {\n enableTimeToInitialDisplayForPreloadedRoutes =\n getReactNavigationIntegration(client)?.options.enableTimeToInitialDisplayForPreloadedRoutes ?? false;\n },\n processEvent: async event => {\n if (event.type !== 'transaction') {\n // TimeToDisplay data is only relevant for transactions\n return event;\n }\n\n const rootSpanId = event.contexts.trace.span_id;\n if (!rootSpanId) {\n logger.warn(`[${INTEGRATION_NAME}] No root span id found in transaction.`);\n return event;\n }\n\n const transactionStartTimestampSeconds = event.start_timestamp;\n if (!transactionStartTimestampSeconds) {\n // This should never happen\n logger.warn(`[${INTEGRATION_NAME}] No transaction start timestamp found in transaction.`);\n return event;\n }\n\n event.spans = event.spans || [];\n event.measurements = event.measurements || {};\n\n const ttidSpan = await addTimeToInitialDisplay({\n event,\n rootSpanId,\n transactionStartTimestampSeconds,\n enableTimeToInitialDisplayForPreloadedRoutes,\n });\n const ttfdSpan = await addTimeToFullDisplay({ event, rootSpanId, transactionStartTimestampSeconds, ttidSpan });\n\n if (ttidSpan && ttidSpan.start_timestamp && ttidSpan.timestamp) {\n event.measurements['time_to_initial_display'] = {\n value: (ttidSpan.timestamp - ttidSpan.start_timestamp) * 1000,\n unit: 'millisecond',\n };\n }\n\n if (ttfdSpan && ttfdSpan.start_timestamp && ttfdSpan.timestamp) {\n const durationMs = (ttfdSpan.timestamp - ttfdSpan.start_timestamp) * 1000;\n if (isDeadlineExceeded(durationMs)) {\n event.measurements['time_to_full_display'] = event.measurements['time_to_initial_display'];\n } else {\n event.measurements['time_to_full_display'] = {\n value: durationMs,\n unit: 'millisecond',\n };\n }\n }\n\n const newTransactionEndTimestampSeconds = Math.max(\n ttidSpan?.timestamp ?? -1,\n ttfdSpan?.timestamp ?? -1,\n event.timestamp ?? -1,\n );\n if (newTransactionEndTimestampSeconds !== -1) {\n event.timestamp = newTransactionEndTimestampSeconds;\n }\n\n return event;\n },\n };\n};\n\nasync function addTimeToInitialDisplay({\n event,\n rootSpanId,\n transactionStartTimestampSeconds,\n enableTimeToInitialDisplayForPreloadedRoutes,\n}: {\n event: Event;\n rootSpanId: string;\n transactionStartTimestampSeconds: number;\n enableTimeToInitialDisplayForPreloadedRoutes: boolean;\n}): Promise<SpanJSON | undefined> {\n const ttidEndTimestampSeconds = await NATIVE.popTimeToDisplayFor(`ttid-${rootSpanId}`);\n\n let ttidSpan: SpanJSON | undefined = event.spans?.find(span => span.op === UI_LOAD_INITIAL_DISPLAY);\n\n if (ttidSpan && (ttidSpan.status === undefined || ttidSpan.status === 'ok') && !ttidEndTimestampSeconds) {\n logger.debug(`[${INTEGRATION_NAME}] Ttid span already exists and is ok.`, ttidSpan);\n return ttidSpan;\n }\n\n if (!ttidEndTimestampSeconds) {\n logger.debug(`[${INTEGRATION_NAME}] No manual ttid end timestamp found for span ${rootSpanId}.`);\n return addAutomaticTimeToInitialDisplay({\n event,\n rootSpanId,\n transactionStartTimestampSeconds,\n enableTimeToInitialDisplayForPreloadedRoutes,\n });\n }\n\n if (ttidSpan && ttidSpan.status && ttidSpan.status !== 'ok') {\n ttidSpan.status = 'ok';\n ttidSpan.timestamp = ttidEndTimestampSeconds;\n logger.debug(`[${INTEGRATION_NAME}] Updated existing ttid span.`, ttidSpan);\n return ttidSpan;\n }\n\n ttidSpan = createSpanJSON({\n op: UI_LOAD_INITIAL_DISPLAY,\n description: 'Time To Initial Display',\n start_timestamp: transactionStartTimestampSeconds,\n timestamp: ttidEndTimestampSeconds,\n origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,\n parent_span_id: rootSpanId,\n data: {\n [SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,\n },\n });\n logger.debug(`[${INTEGRATION_NAME}] Added ttid span to transaction.`, ttidSpan);\n event.spans.push(ttidSpan);\n return ttidSpan;\n}\n\nasync function addAutomaticTimeToInitialDisplay({\n event,\n rootSpanId,\n transactionStartTimestampSeconds,\n enableTimeToInitialDisplayForPreloadedRoutes,\n}: {\n event: Event;\n rootSpanId: string;\n transactionStartTimestampSeconds: number;\n enableTimeToInitialDisplayForPreloadedRoutes: boolean;\n}): Promise<SpanJSON | undefined> {\n const ttidNativeTimestampSeconds = await NATIVE.popTimeToDisplayFor(`ttid-navigation-${rootSpanId}`);\n const ttidFallbackTimestampSeconds = await getTimeToInitialDisplayFallback(rootSpanId);\n\n const hasBeenSeen = event.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_ROUTE_HAS_BEEN_SEEN];\n if (hasBeenSeen && !enableTimeToInitialDisplayForPreloadedRoutes) {\n logger.debug(\n `[${INTEGRATION_NAME}] Route has been seen and time to initial display is disabled for preloaded routes.`,\n );\n return undefined;\n }\n\n const ttidTimestampSeconds = ttidNativeTimestampSeconds ?? ttidFallbackTimestampSeconds;\n if (!ttidTimestampSeconds) {\n logger.debug(`[${INTEGRATION_NAME}] No automatic ttid end timestamp found for span ${rootSpanId}.`);\n return undefined;\n }\n\n const viewNames = event.contexts?.app?.view_names;\n const screenName = Array.isArray(viewNames) ? viewNames[0] : viewNames;\n\n const ttidSpan = createSpanJSON({\n op: UI_LOAD_INITIAL_DISPLAY,\n description: screenName ? `${screenName} initial display` : 'Time To Initial Display',\n start_timestamp: transactionStartTimestampSeconds,\n timestamp: ttidTimestampSeconds,\n origin: SPAN_ORIGIN_AUTO_UI_TIME_TO_DISPLAY,\n parent_span_id: rootSpanId,\n data: {\n [SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,\n },\n });\n event.spans = event.spans ?? [];\n event.spans.push(ttidSpan);\n return ttidSpan;\n}\n\nasync function addTimeToFullDisplay({\n event,\n rootSpanId,\n transactionStartTimestampSeconds,\n ttidSpan,\n}: {\n event: Event;\n rootSpanId: string;\n transactionStartTimestampSeconds: number;\n ttidSpan: SpanJSON | undefined;\n}): Promise<SpanJSON | undefined> {\n const ttfdEndTimestampSeconds = await NATIVE.popTimeToDisplayFor(`ttfd-${rootSpanId}`);\n\n if (!ttidSpan || !ttfdEndTimestampSeconds) {\n return undefined;\n }\n\n let ttfdSpan = event.spans?.find(span => span.op === UI_LOAD_FULL_DISPLAY);\n\n let ttfdAdjustedEndTimestampSeconds = ttfdEndTimestampSeconds;\n const ttfdIsBeforeTtid = ttidSpan?.timestamp && ttfdEndTimestampSeconds < ttidSpan.timestamp;\n if (ttfdIsBeforeTtid) {\n ttfdAdjustedEndTimestampSeconds = ttidSpan.timestamp;\n }\n\n const durationMs = (ttfdAdjustedEndTimestampSeconds - transactionStartTimestampSeconds) * 1000;\n\n if (ttfdSpan && ttfdSpan.status && ttfdSpan.status !== 'ok') {\n ttfdSpan.status = 'ok';\n ttfdSpan.timestamp = ttfdAdjustedEndTimestampSeconds;\n logger.debug(`[${INTEGRATION_NAME}] Updated existing ttfd span.`, ttfdSpan);\n return ttfdSpan;\n }\n\n ttfdSpan = createSpanJSON({\n status: isDeadlineExceeded(durationMs) ? 'deadline_exceeded' : 'ok',\n op: UI_LOAD_FULL_DISPLAY,\n description: 'Time To Full Display',\n start_timestamp: transactionStartTimestampSeconds,\n timestamp: ttfdAdjustedEndTimestampSeconds,\n origin: SPAN_ORIGIN_MANUAL_UI_TIME_TO_DISPLAY,\n parent_span_id: rootSpanId,\n data: {\n [SPAN_THREAD_NAME]: SPAN_THREAD_NAME_JAVASCRIPT,\n },\n });\n logger.debug(`[${INTEGRATION_NAME}] Added ttfd span to transaction.`, ttfdSpan);\n event.spans.push(ttfdSpan);\n return ttfdSpan;\n}\n"]}
|
package/dist/js/tracing/ops.d.ts
CHANGED
|
@@ -5,4 +5,6 @@ export declare const UI_ACTION = "ui.action";
|
|
|
5
5
|
export declare const UI_ACTION_TOUCH = "ui.action.touch";
|
|
6
6
|
export declare const APP_START_COLD = "app.start.cold";
|
|
7
7
|
export declare const APP_START_WARM = "app.start.warm";
|
|
8
|
+
export declare const UI_LOAD_INITIAL_DISPLAY = "ui.load.initial_display";
|
|
9
|
+
export declare const UI_LOAD_FULL_DISPLAY = "ui.load.full_display";
|
|
8
10
|
//# sourceMappingURL=ops.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/ops.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/ops.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AACjE,eAAO,MAAM,oBAAoB,yBAAyB,CAAC"}
|
package/dist/js/tracing/ops.js
CHANGED
|
@@ -5,4 +5,6 @@ export const UI_ACTION = 'ui.action';
|
|
|
5
5
|
export const UI_ACTION_TOUCH = 'ui.action.touch';
|
|
6
6
|
export const APP_START_COLD = 'app.start.cold';
|
|
7
7
|
export const APP_START_WARM = 'app.start.warm';
|
|
8
|
+
export const UI_LOAD_INITIAL_DISPLAY = 'ui.load.initial_display';
|
|
9
|
+
export const UI_LOAD_FULL_DISPLAY = 'ui.load.full_display';
|
|
8
10
|
//# sourceMappingURL=ops.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.js","sourceRoot":"","sources":["../../../src/js/tracing/ops.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC;AAEvC,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC;AACjC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEjD,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAC/C,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC","sourcesContent":["export const DEFAULT = 'default';\nexport const NAVIGATION = 'navigation';\n\nexport const UI_LOAD = 'ui.load';\nexport const UI_ACTION = 'ui.action';\nexport const UI_ACTION_TOUCH = 'ui.action.touch';\n\nexport const APP_START_COLD = 'app.start.cold';\nexport const APP_START_WARM = 'app.start.warm';\n"]}
|
|
1
|
+
{"version":3,"file":"ops.js","sourceRoot":"","sources":["../../../src/js/tracing/ops.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC;AAEvC,MAAM,CAAC,MAAM,OAAO,GAAG,SAAS,CAAC;AACjC,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAEjD,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAC/C,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AACjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC","sourcesContent":["export const DEFAULT = 'default';\nexport const NAVIGATION = 'navigation';\n\nexport const UI_LOAD = 'ui.load';\nexport const UI_ACTION = 'ui.action';\nexport const UI_ACTION_TOUCH = 'ui.action.touch';\n\nexport const APP_START_COLD = 'app.start.cold';\nexport const APP_START_WARM = 'app.start.warm';\n\nexport const UI_LOAD_INITIAL_DISPLAY = 'ui.load.initial_display';\nexport const UI_LOAD_FULL_DISPLAY = 'ui.load.full_display';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactnativeprofiler.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnativeprofiler.tsx"],"names":[],"mappings":"AACA,OAAO,EAAa,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"reactnativeprofiler.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnativeprofiler.tsx"],"names":[],"mappings":"AACA,OAAO,EAAa,QAAQ,EAAE,MAAM,eAAe,CAAC;AAapD;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,SAAgB,IAAI,EAAE,MAAM,CAAyB;gBAElC,KAAK,EAAE,qBAAqB,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;IAKnE;;OAEG;IACI,iBAAiB,IAAI,IAAI;IAQhC;;OAEG;IACH,OAAO,CAAC,eAAe;CAsBxB"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { timestampInSeconds } from '@sentry/core';
|
|
1
|
+
import { logger, timestampInSeconds } from '@sentry/core';
|
|
2
2
|
import { getClient, Profiler } from '@sentry/react';
|
|
3
|
+
import { getAppRegistryIntegration } from '../integrations/appRegistry';
|
|
3
4
|
import { createIntegration } from '../integrations/factory';
|
|
4
5
|
import { _captureAppStart, _setRootComponentCreationTimestampMs } from '../tracing/integrations/appStart';
|
|
5
6
|
const ReactNativeProfilerGlobalState = {
|
|
6
7
|
appStartReported: false,
|
|
8
|
+
onRunApplicationHook: () => {
|
|
9
|
+
ReactNativeProfilerGlobalState.appStartReported = false;
|
|
10
|
+
},
|
|
7
11
|
};
|
|
8
12
|
/**
|
|
9
13
|
* Custom profiler for the React Native app root.
|
|
@@ -36,6 +40,13 @@ export class ReactNativeProfiler extends Profiler {
|
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
42
|
client.addIntegration && client.addIntegration(createIntegration(this.name));
|
|
43
|
+
const appRegistryIntegration = getAppRegistryIntegration(client);
|
|
44
|
+
if (appRegistryIntegration && typeof appRegistryIntegration.onRunApplication === 'function') {
|
|
45
|
+
appRegistryIntegration.onRunApplication(ReactNativeProfilerGlobalState.onRunApplicationHook);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
logger.warn('AppRegistryIntegration.onRunApplication not found or invalid.');
|
|
49
|
+
}
|
|
39
50
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
40
51
|
_captureAppStart({ isManual: false });
|
|
41
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactnativeprofiler.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnativeprofiler.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"reactnativeprofiler.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnativeprofiler.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,oCAAoC,EAAE,MAAM,kCAAkC,CAAC;AAE1G,MAAM,8BAA8B,GAAG;IACrC,gBAAgB,EAAE,KAAK;IACvB,oBAAoB,EAAE,GAAG,EAAE;QACzB,8BAA8B,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAC1D,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAG/C,YAAmB,KAAgD;QACjE,oCAAoC,CAAC,kBAAkB,EAAE,GAAG,IAAI,CAAC,CAAC;QAClE,KAAK,CAAC,KAAK,CAAC,CAAC;QAJC,SAAI,GAAW,qBAAqB,CAAC;IAKrD,CAAC;IAED;;OAEG;IACI,iBAAiB;QACtB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,8BAA8B,CAAC,gBAAgB,EAAE;YACpD,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,8BAA8B,CAAC,gBAAgB,GAAG,IAAI,CAAC;SACxD;IACH,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAE3B,IAAI,CAAC,MAAM,EAAE;YACX,iFAAiF;YACjF,sCAAsC;YACtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;YAChH,OAAO;SACR;QAED,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE7E,MAAM,sBAAsB,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;QACjE,IAAI,sBAAsB,IAAI,OAAO,sBAAsB,CAAC,gBAAgB,KAAK,UAAU,EAAE;YAC3F,sBAAsB,CAAC,gBAAgB,CAAC,8BAA8B,CAAC,oBAAoB,CAAC,CAAC;SAC9F;aAAM;YACL,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;SAC9E;QAED,mEAAmE;QACnE,gBAAgB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;CACF","sourcesContent":["import { logger, timestampInSeconds } from '@sentry/core';\nimport { getClient, Profiler } from '@sentry/react';\n\nimport { getAppRegistryIntegration } from '../integrations/appRegistry';\nimport { createIntegration } from '../integrations/factory';\nimport { _captureAppStart, _setRootComponentCreationTimestampMs } from '../tracing/integrations/appStart';\n\nconst ReactNativeProfilerGlobalState = {\n appStartReported: false,\n onRunApplicationHook: () => {\n ReactNativeProfilerGlobalState.appStartReported = false;\n },\n};\n\n/**\n * Custom profiler for the React Native app root.\n */\nexport class ReactNativeProfiler extends Profiler {\n public readonly name: string = 'ReactNativeProfiler';\n\n public constructor(props: ConstructorParameters<typeof Profiler>[0]) {\n _setRootComponentCreationTimestampMs(timestampInSeconds() * 1000);\n super(props);\n }\n\n /**\n * Get the app root mount time.\n */\n public componentDidMount(): void {\n super.componentDidMount();\n if (!ReactNativeProfilerGlobalState.appStartReported) {\n this._reportAppStart();\n ReactNativeProfilerGlobalState.appStartReported = true;\n }\n }\n\n /**\n * Notifies the Tracing integration that the app start has finished.\n */\n private _reportAppStart(): void {\n const client = getClient();\n\n if (!client) {\n // We can't use logger here because this will be logged before the `Sentry.init`.\n // eslint-disable-next-line no-console\n __DEV__ && console.warn('App Start Span could not be finished. `Sentry.wrap` was called before `Sentry.init`.');\n return;\n }\n\n client.addIntegration && client.addIntegration(createIntegration(this.name));\n\n const appRegistryIntegration = getAppRegistryIntegration(client);\n if (appRegistryIntegration && typeof appRegistryIntegration.onRunApplication === 'function') {\n appRegistryIntegration.onRunApplication(ReactNativeProfilerGlobalState.onRunApplicationHook);\n } else {\n logger.warn('AppRegistryIntegration.onRunApplication not found or invalid.');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n _captureAppStart({ isManual: false });\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Integration } from '@sentry/core';
|
|
1
|
+
import type { Client, Integration } from '@sentry/core';
|
|
2
2
|
export declare const INTEGRATION_NAME = "ReactNavigation";
|
|
3
3
|
interface ReactNavigationIntegrationOptions {
|
|
4
4
|
/**
|
|
@@ -22,6 +22,19 @@ interface ReactNavigationIntegrationOptions {
|
|
|
22
22
|
* @default true
|
|
23
23
|
*/
|
|
24
24
|
ignoreEmptyBackNavigationTransactions: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Enabled measuring Time to Initial Display for routes that are already loaded in memory.
|
|
27
|
+
* (a.k.a., Routes that the navigation integration has already seen.)
|
|
28
|
+
*
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
enableTimeToInitialDisplayForPreloadedRoutes: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether to use the dispatched action data to populate the transaction metadata.
|
|
34
|
+
*
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
useDispatchedActionData: boolean;
|
|
25
38
|
}
|
|
26
39
|
/**
|
|
27
40
|
* Instrumentation for React-Navigation V5 and above. See docs or sample app for usage.
|
|
@@ -31,17 +44,22 @@ interface ReactNavigationIntegrationOptions {
|
|
|
31
44
|
* - `_onStateChange` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.
|
|
32
45
|
* - If `_onStateChange` isn't called within `STATE_CHANGE_TIMEOUT_DURATION` of the dispatch, then the transaction is not sampled and finished.
|
|
33
46
|
*/
|
|
34
|
-
export declare const reactNavigationIntegration: ({ routeChangeTimeoutMs, enableTimeToInitialDisplay, ignoreEmptyBackNavigationTransactions, }?: Partial<ReactNavigationIntegrationOptions>) => Integration & {
|
|
47
|
+
export declare const reactNavigationIntegration: ({ routeChangeTimeoutMs, enableTimeToInitialDisplay, ignoreEmptyBackNavigationTransactions, enableTimeToInitialDisplayForPreloadedRoutes, useDispatchedActionData, }?: Partial<ReactNavigationIntegrationOptions>) => Integration & {
|
|
35
48
|
/**
|
|
36
49
|
* Pass the ref to the navigation container to register it to the instrumentation
|
|
37
50
|
* @param navigationContainerRef Ref to a `NavigationContainer`
|
|
38
51
|
*/
|
|
39
52
|
registerNavigationContainer: (navigationContainerRef: unknown) => void;
|
|
53
|
+
options: ReactNavigationIntegrationOptions;
|
|
40
54
|
};
|
|
41
55
|
export interface NavigationRoute {
|
|
42
56
|
name: string;
|
|
43
57
|
key: string;
|
|
44
58
|
params?: Record<string, any>;
|
|
45
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Returns React Navigation integration of the given client.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getReactNavigationIntegration(client: Client): ReturnType<typeof reactNavigationIntegration> | undefined;
|
|
46
64
|
export {};
|
|
47
65
|
//# sourceMappingURL=reactnavigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactnavigation.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"reactnavigation.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAQ,MAAM,cAAc,CAAC;AA+B9D,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAIlD,UAAU,iCAAiC;IACzC;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,0BAA0B,EAAE,OAAO,CAAC;IAEpC;;;;;OAKG;IACH,qCAAqC,EAAE,OAAO,CAAC;IAE/C;;;;;OAKG;IACH,4CAA4C,EAAE,OAAO,CAAC;IAEtD;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,yKAMpC,QAAQ,iCAAiC,CAAC;IAC3C;;;OAGG;0DACmD,OAAO,KAAK,IAAI;aAC7D,iCAAiC;CA0S3C,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IAEZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAOD;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,GACb,UAAU,CAAC,OAAO,0BAA0B,CAAC,GAAG,SAAS,CAE3D"}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { addBreadcrumb,
|
|
2
|
-
import {
|
|
1
|
+
import { addBreadcrumb, getClient, isPlainObject, logger, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_OK, spanToJSON, startInactiveSpan, timestampInSeconds, } from '@sentry/core';
|
|
2
|
+
import { getAppRegistryIntegration } from '../integrations/appRegistry';
|
|
3
3
|
import { isSentrySpan } from '../utils/span';
|
|
4
4
|
import { RN_GLOBAL_OBJ } from '../utils/worldwide';
|
|
5
5
|
import { NATIVE } from '../wrapper';
|
|
6
6
|
import { ignoreEmptyBackNavigation } from './onSpanEndUtils';
|
|
7
7
|
import { SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION } from './origin';
|
|
8
8
|
import { getReactNativeTracingIntegration } from './reactnativetracing';
|
|
9
|
-
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes';
|
|
9
|
+
import { SEMANTIC_ATTRIBUTE_NAVIGATION_ACTION_TYPE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes';
|
|
10
10
|
import { DEFAULT_NAVIGATION_SPAN_NAME, defaultIdleOptions, getDefaultIdleNavigationSpanOptions, startIdleNavigationSpan as startGenericIdleNavigationSpan, } from './span';
|
|
11
|
-
import {
|
|
12
|
-
import { setSpanDurationAsMeasurementOnSpan } from './utils';
|
|
11
|
+
import { addTimeToInitialDisplayFallback } from './timeToDisplayFallback';
|
|
13
12
|
export const INTEGRATION_NAME = 'ReactNavigation';
|
|
14
13
|
const NAVIGATION_HISTORY_MAX_SIZE = 200;
|
|
15
14
|
/**
|
|
@@ -20,9 +19,8 @@ const NAVIGATION_HISTORY_MAX_SIZE = 200;
|
|
|
20
19
|
* - `_onStateChange` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.
|
|
21
20
|
* - If `_onStateChange` isn't called within `STATE_CHANGE_TIMEOUT_DURATION` of the dispatch, then the transaction is not sampled and finished.
|
|
22
21
|
*/
|
|
23
|
-
export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enableTimeToInitialDisplay = false, ignoreEmptyBackNavigationTransactions = true, } = {}) => {
|
|
22
|
+
export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enableTimeToInitialDisplay = false, ignoreEmptyBackNavigationTransactions = true, enableTimeToInitialDisplayForPreloadedRoutes = false, useDispatchedActionData = false, } = {}) => {
|
|
24
23
|
let navigationContainer;
|
|
25
|
-
let newScreenFrameEventEmitter;
|
|
26
24
|
let tracing;
|
|
27
25
|
let idleSpanOptions = defaultIdleOptions;
|
|
28
26
|
let latestRoute;
|
|
@@ -32,8 +30,6 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
32
30
|
let stateChangeTimeout;
|
|
33
31
|
let recentRouteKeys = [];
|
|
34
32
|
if (enableTimeToInitialDisplay) {
|
|
35
|
-
newScreenFrameEventEmitter = createSentryFallbackEventEmitter();
|
|
36
|
-
newScreenFrameEventEmitter.initAsync();
|
|
37
33
|
NATIVE.initNativeReactNavigationNewFrameTracking().catch((reason) => {
|
|
38
34
|
logger.error(`${INTEGRATION_NAME} Failed to initialize native new frame tracking: ${reason}`);
|
|
39
35
|
});
|
|
@@ -42,6 +38,7 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
42
38
|
* Set the initial state and start initial navigation span for the current screen.
|
|
43
39
|
*/
|
|
44
40
|
const afterAllSetup = (client) => {
|
|
41
|
+
var _a;
|
|
45
42
|
tracing = getReactNativeTracingIntegration(client);
|
|
46
43
|
if (tracing) {
|
|
47
44
|
idleSpanOptions = {
|
|
@@ -51,8 +48,19 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
51
48
|
}
|
|
52
49
|
if (initialStateHandled) {
|
|
53
50
|
// We create an initial state here to ensure a transaction gets created before the first route mounts.
|
|
51
|
+
// This assumes that the Sentry.init() call is made before the first route mounts.
|
|
52
|
+
// If this is not the case, the first transaction will be nameless 'Route Changed'
|
|
54
53
|
return undefined;
|
|
55
54
|
}
|
|
55
|
+
(_a = getAppRegistryIntegration(client)) === null || _a === void 0 ? void 0 : _a.onRunApplication(() => {
|
|
56
|
+
if (initialStateHandled) {
|
|
57
|
+
// To avoid conflict with the initial transaction we check if it was already handled.
|
|
58
|
+
// This ensures runApplication calls after the initial start are correctly traced.
|
|
59
|
+
// This is used for example when Activity is (re)started on Android.
|
|
60
|
+
logger.log('[ReactNavigationIntegration] Starting new idle navigation span based on runApplication call.');
|
|
61
|
+
startIdleNavigationSpan();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
56
64
|
startIdleNavigationSpan();
|
|
57
65
|
if (!navigationContainer) {
|
|
58
66
|
// This is expected as navigation container is registered after the root component is mounted.
|
|
@@ -62,22 +70,26 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
62
70
|
updateLatestNavigationSpanWithCurrentRoute();
|
|
63
71
|
initialStateHandled = true;
|
|
64
72
|
};
|
|
65
|
-
const registerNavigationContainer = (
|
|
66
|
-
/* We prevent duplicate routing instrumentation to be initialized on fast refreshes
|
|
67
|
-
|
|
68
|
-
Explanation: If the user triggers a fast refresh on the file that the instrumentation is
|
|
69
|
-
initialized in, it will initialize a new instance and will cause undefined behavior.
|
|
70
|
-
*/
|
|
73
|
+
const registerNavigationContainer = (maybeNewNavigationContainer) => {
|
|
71
74
|
if (RN_GLOBAL_OBJ.__sentry_rn_v5_registered) {
|
|
72
|
-
logger.
|
|
73
|
-
|
|
75
|
+
logger.debug(`${INTEGRATION_NAME} Instrumentation already exists, but registering again...`);
|
|
76
|
+
// In the past we have not allowed re-registering the navigation container to avoid unexpected behavior.
|
|
77
|
+
// But this doesn't work for Android and re-recreating application main activity.
|
|
78
|
+
// Where new navigation container is created and the old one is discarded. We need to re-register to
|
|
79
|
+
// trace the new navigation container navigation.
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
let newNavigationContainer;
|
|
82
|
+
if (isPlainObject(maybeNewNavigationContainer) && 'current' in maybeNewNavigationContainer) {
|
|
83
|
+
newNavigationContainer = maybeNewNavigationContainer.current;
|
|
77
84
|
}
|
|
78
85
|
else {
|
|
79
|
-
|
|
86
|
+
newNavigationContainer = maybeNewNavigationContainer;
|
|
80
87
|
}
|
|
88
|
+
if (navigationContainer === newNavigationContainer) {
|
|
89
|
+
logger.log(`${INTEGRATION_NAME} Navigation container ref is the same as the one already registered.`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
navigationContainer = newNavigationContainer;
|
|
81
93
|
if (!navigationContainer) {
|
|
82
94
|
logger.warn(`${INTEGRATION_NAME} Received invalid navigation container ref!`);
|
|
83
95
|
return undefined;
|
|
@@ -104,7 +116,26 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
104
116
|
* It does not name the transaction or populate it with route information. Instead, it waits for the state to fully change
|
|
105
117
|
* and gets the route information from there, @see updateLatestNavigationSpanWithCurrentRoute
|
|
106
118
|
*/
|
|
107
|
-
const startIdleNavigationSpan = () => {
|
|
119
|
+
const startIdleNavigationSpan = (unknownEvent) => {
|
|
120
|
+
const event = unknownEvent;
|
|
121
|
+
if (useDispatchedActionData && (event === null || event === void 0 ? void 0 : event.data.noop)) {
|
|
122
|
+
logger.debug(`${INTEGRATION_NAME} Navigation action is a noop, not starting navigation span.`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const navigationActionType = useDispatchedActionData ? event === null || event === void 0 ? void 0 : event.data.action.type : undefined;
|
|
126
|
+
if (useDispatchedActionData &&
|
|
127
|
+
[
|
|
128
|
+
// Process common actions
|
|
129
|
+
'PRELOAD',
|
|
130
|
+
'SET_PARAMS',
|
|
131
|
+
// Drawer actions
|
|
132
|
+
'OPEN_DRAWER',
|
|
133
|
+
'CLOSE_DRAWER',
|
|
134
|
+
'TOGGLE_DRAWER',
|
|
135
|
+
].includes(navigationActionType)) {
|
|
136
|
+
logger.debug(`${INTEGRATION_NAME} Navigation action is ${navigationActionType}, not starting navigation span.`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
108
139
|
if (latestNavigationSpan) {
|
|
109
140
|
logger.log(`${INTEGRATION_NAME} A transaction was detected that turned out to be a noop, discarding.`);
|
|
110
141
|
_discardLatestTransaction();
|
|
@@ -114,10 +145,12 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
114
145
|
? tracing.options.beforeStartSpan(getDefaultIdleNavigationSpanOptions())
|
|
115
146
|
: getDefaultIdleNavigationSpanOptions(), idleSpanOptions);
|
|
116
147
|
latestNavigationSpan === null || latestNavigationSpan === void 0 ? void 0 : latestNavigationSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_ORIGIN_AUTO_NAVIGATION_REACT_NAVIGATION);
|
|
148
|
+
latestNavigationSpan === null || latestNavigationSpan === void 0 ? void 0 : latestNavigationSpan.setAttribute(SEMANTIC_ATTRIBUTE_NAVIGATION_ACTION_TYPE, navigationActionType);
|
|
117
149
|
if (ignoreEmptyBackNavigationTransactions) {
|
|
118
150
|
ignoreEmptyBackNavigation(getClient(), latestNavigationSpan);
|
|
119
151
|
}
|
|
120
152
|
if (enableTimeToInitialDisplay) {
|
|
153
|
+
NATIVE.setActiveSpanId(latestNavigationSpan === null || latestNavigationSpan === void 0 ? void 0 : latestNavigationSpan.spanContext().spanId);
|
|
121
154
|
navigationProcessingSpan = startInactiveSpan({
|
|
122
155
|
op: 'navigation.processing',
|
|
123
156
|
name: 'Navigation dispatch to navigation cancelled or screen mounted',
|
|
@@ -146,6 +179,7 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
146
179
|
logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but navigation transaction was not started on dispatch.`);
|
|
147
180
|
return undefined;
|
|
148
181
|
}
|
|
182
|
+
addTimeToInitialDisplayFallback(latestNavigationSpan.spanContext().spanId, NATIVE.getNewScreenTimeToDisplay());
|
|
149
183
|
if (previousRoute && previousRoute.key === route.key) {
|
|
150
184
|
logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but route is the same as previous.`);
|
|
151
185
|
pushRecentRouteKey(route.key);
|
|
@@ -155,25 +189,6 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
155
189
|
return undefined;
|
|
156
190
|
}
|
|
157
191
|
const routeHasBeenSeen = recentRouteKeys.includes(route.key);
|
|
158
|
-
const latestTtidSpan = !routeHasBeenSeen &&
|
|
159
|
-
enableTimeToInitialDisplay &&
|
|
160
|
-
startTimeToInitialDisplaySpan({
|
|
161
|
-
name: `${route.name} initial display`,
|
|
162
|
-
isAutoInstrumented: true,
|
|
163
|
-
});
|
|
164
|
-
const navigationSpanWithTtid = latestNavigationSpan;
|
|
165
|
-
if (!routeHasBeenSeen && latestTtidSpan) {
|
|
166
|
-
newScreenFrameEventEmitter === null || newScreenFrameEventEmitter === void 0 ? void 0 : newScreenFrameEventEmitter.onceNewFrame(({ newFrameTimestampInSeconds }) => {
|
|
167
|
-
const activeSpan = getActiveSpan();
|
|
168
|
-
if (activeSpan && manualInitialDisplaySpans.has(activeSpan)) {
|
|
169
|
-
logger.warn('[ReactNavigationInstrumentation] Detected manual instrumentation for the current active span.');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
latestTtidSpan.setStatus({ code: SPAN_STATUS_OK });
|
|
173
|
-
latestTtidSpan.end(newFrameTimestampInSeconds);
|
|
174
|
-
setSpanDurationAsMeasurementOnSpan('time_to_initial_display', latestTtidSpan, navigationSpanWithTtid);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
192
|
navigationProcessingSpan === null || navigationProcessingSpan === void 0 ? void 0 : navigationProcessingSpan.updateName(`Navigation dispatch to screen ${route.name} mounted`);
|
|
178
193
|
navigationProcessingSpan === null || navigationProcessingSpan === void 0 ? void 0 : navigationProcessingSpan.setStatus({ code: SPAN_STATUS_OK });
|
|
179
194
|
navigationProcessingSpan === null || navigationProcessingSpan === void 0 ? void 0 : navigationProcessingSpan.end(stateChangedTimestamp);
|
|
@@ -242,6 +257,19 @@ export const reactNavigationIntegration = ({ routeChangeTimeoutMs = 1000, enable
|
|
|
242
257
|
name: INTEGRATION_NAME,
|
|
243
258
|
afterAllSetup,
|
|
244
259
|
registerNavigationContainer,
|
|
260
|
+
options: {
|
|
261
|
+
routeChangeTimeoutMs,
|
|
262
|
+
enableTimeToInitialDisplay,
|
|
263
|
+
ignoreEmptyBackNavigationTransactions,
|
|
264
|
+
enableTimeToInitialDisplayForPreloadedRoutes,
|
|
265
|
+
useDispatchedActionData,
|
|
266
|
+
},
|
|
245
267
|
};
|
|
246
268
|
};
|
|
269
|
+
/**
|
|
270
|
+
* Returns React Navigation integration of the given client.
|
|
271
|
+
*/
|
|
272
|
+
export function getReactNavigationIntegration(client) {
|
|
273
|
+
return client.getIntegrationByName(INTEGRATION_NAME);
|
|
274
|
+
}
|
|
247
275
|
//# sourceMappingURL=reactnavigation.js.map
|