Skip to content

Commit 477b221

Browse files
elirangoshenclaude
andcommitted
Add Sentry span for JS bundle parse time
Uses native `runJsBundleStart`/`runJsBundleEnd` performance marks to create a `ManualJsParseTime` span parented to `ManualAppStartup`, making pre-JS startup time visible in Sentry traces. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 37dbdd1 commit 477b221

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,6 +1777,7 @@ const CONST = {
17771777
// Span names
17781778
SPAN_OPEN_REPORT: 'ManualOpenReport',
17791779
SPAN_APP_STARTUP: 'ManualAppStartup',
1780+
SPAN_JS_PARSE_TIME: 'ManualJsParseTime',
17801781
SPAN_NAVIGATE_TO_REPORTS_TAB: 'ManualNavigateToReportsTab',
17811782
SPAN_NAVIGATE_TO_REPORTS_TAB_RENDER: 'ManualNavigateToReportsTabRender',
17821783
SPAN_ON_LAYOUT_SKELETON_REPORTS: 'ManualOnLayoutSkeletonReports',

src/libs/telemetry/activeSpans.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {StartSpanOptions} from '@sentry/core';
1+
import type {SpanTimeInput, StartSpanOptions} from '@sentry/core';
22
import * as Sentry from '@sentry/react-native';
33
import CONST from '@src/CONST';
44

@@ -25,15 +25,15 @@ function startSpan(spanId: string, options: StartSpanOptions, extraOptions: Star
2525
return span;
2626
}
2727

28-
function endSpan(spanId: string) {
28+
function endSpan(spanId: string, endTime?: SpanTimeInput) {
2929
const span = activeSpans.get(spanId);
3030

3131
if (!span) {
3232
return;
3333
}
3434
span.setStatus({code: 1});
3535
span.setAttribute(CONST.TELEMETRY.ATTRIBUTE_FINISHED_MANUALLY, true);
36-
span.end();
36+
span.end(endTime);
3737
activeSpans.delete(spanId);
3838
}
3939

src/setup/telemetry/index.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as Sentry from '@sentry/react-native';
22
import {Platform} from 'react-native';
3+
import performance, {PerformanceObserver} from 'react-native-performance';
34
import {isDevelopment} from '@libs/Environment/Environment';
4-
import {startSpan} from '@libs/telemetry/activeSpans';
5+
import {endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans';
56
import {breadcrumbsIntegration, browserProfilingIntegration, consoleIntegration, navigationIntegration, tracingIntegration} from '@libs/telemetry/integrations';
67
import processBeforeSendTransactions from '@libs/telemetry/middlewares';
78
import CONFIG from '@src/CONFIG';
@@ -39,4 +40,34 @@ export default function (): void {
3940
name: CONST.TELEMETRY.SPAN_APP_STARTUP,
4041
op: CONST.TELEMETRY.SPAN_APP_STARTUP,
4142
});
43+
44+
const runJsBundleStartEntries = performance.getEntriesByName('runJsBundleStart');
45+
if (runJsBundleStartEntries.length > 0) {
46+
const jsParseStartSecs = (runJsBundleStartEntries.at(0)?.startTime ?? 0) / 1000;
47+
48+
startSpan(CONST.TELEMETRY.SPAN_JS_PARSE_TIME, {
49+
name: CONST.TELEMETRY.SPAN_JS_PARSE_TIME,
50+
op: CONST.TELEMETRY.SPAN_JS_PARSE_TIME,
51+
startTime: jsParseStartSecs,
52+
parentSpan: getSpan(CONST.TELEMETRY.SPAN_APP_STARTUP),
53+
});
54+
55+
const finishJsParseSpan = (endTimeSecs: number) => {
56+
endSpan(CONST.TELEMETRY.SPAN_JS_PARSE_TIME, endTimeSecs);
57+
};
58+
59+
const runJsBundleEndEntries = performance.getEntriesByName('runJsBundleEnd');
60+
if (runJsBundleEndEntries.length > 0) {
61+
finishJsParseSpan((runJsBundleEndEntries.at(0)?.startTime ?? 0) / 1000);
62+
} else {
63+
const observer = new PerformanceObserver((list) => {
64+
const entries = list.getEntriesByName('runJsBundleEnd');
65+
if (entries.length > 0) {
66+
finishJsParseSpan((entries.at(0)?.startTime ?? 0) / 1000);
67+
observer.disconnect();
68+
}
69+
});
70+
observer.observe({type: 'mark', buffered: true});
71+
}
72+
}
4273
}

0 commit comments

Comments
 (0)