@@ -3,11 +3,19 @@ import type { CustomSamplingContext, Hub, Transaction, TransactionContext } from
33import { logger , uuid4 } from '@sentry/utils' ;
44
55import { WINDOW } from '../helpers' ;
6- import type { JSSelfProfile , JSSelfProfiler , ProcessedJSSelfProfile } from './jsSelfProfiling' ;
6+ import type {
7+ JSSelfProfile ,
8+ JSSelfProfiler ,
9+ JSSelfProfilerConstructor ,
10+ ProcessedJSSelfProfile ,
11+ } from './jsSelfProfiling' ;
712import { sendProfile } from './sendProfile' ;
813
914// Max profile duration.
1015const MAX_PROFILE_DURATION_MS = 30_000 ;
16+ // Keep a flag value to avoid re-initializing the profiler constructor. If it fails
17+ // once, it will always fail and this allows us to early return.
18+ let PROFILING_CONSTRUCTOR_FAILED = false ;
1119
1220// While we experiment, per transaction sampling interval will be more flexible to work with.
1321type StartTransaction = (
@@ -20,7 +28,7 @@ type StartTransaction = (
2028 * Check if profiler constructor is available.
2129 * @param maybeProfiler
2230 */
23- function isJSProfilerSupported ( maybeProfiler : unknown ) : maybeProfiler is typeof JSSelfProfiler {
31+ function isJSProfilerSupported ( maybeProfiler : unknown ) : maybeProfiler is typeof JSSelfProfilerConstructor {
2432 return typeof maybeProfiler === 'function' ;
2533}
2634
@@ -49,8 +57,9 @@ export function onProfilingStartRouteTransaction(transaction: Transaction | unde
4957 */
5058function wrapTransactionWithProfiling ( transaction : Transaction ) : Transaction {
5159 // Feature support check first
52- const JSProfiler = WINDOW . Profiler ;
53- if ( ! isJSProfilerSupported ( JSProfiler ) ) {
60+ const JSProfilerConstructor = WINDOW . Profiler ;
61+
62+ if ( ! isJSProfilerSupported ( JSProfilerConstructor ) ) {
5463 if ( __DEBUG_BUILD__ ) {
5564 logger . log (
5665 '[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.' ,
@@ -67,6 +76,14 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
6776 return transaction ;
6877 }
6978
79+ // If constructor failed once, it will always fail, so we can early return.
80+ if ( PROFILING_CONSTRUCTOR_FAILED ) {
81+ if ( __DEBUG_BUILD__ ) {
82+ logger . log ( '[Profiling] Profiling has been disabled for the duration of the current user session.' ) ;
83+ }
84+ return transaction ;
85+ }
86+
7087 const client = getCurrentHub ( ) . getClient ( ) ;
7188 const options = client && client . getOptions ( ) ;
7289
@@ -91,7 +108,29 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
91108 const samplingIntervalMS = 10 ;
92109 // Start the profiler
93110 const maxSamples = Math . floor ( MAX_PROFILE_DURATION_MS / samplingIntervalMS ) ;
94- const profiler = new JSProfiler ( { sampleInterval : samplingIntervalMS , maxBufferSize : maxSamples } ) ;
111+ let profiler : JSSelfProfiler | undefined ;
112+
113+ // Attempt to initialize the profiler constructor, if it fails, we disable profiling for the current user session.
114+ // This is likely due to a missing 'Document-Policy': 'js-profiling' header. We do not want to throw an error if this happens
115+ // as we risk breaking the user's application, so just disable profiling and log an error.
116+ try {
117+ profiler = new JSProfilerConstructor ( { sampleInterval : samplingIntervalMS , maxBufferSize : maxSamples } ) ;
118+ } catch ( e ) {
119+ if ( __DEBUG_BUILD__ ) {
120+ logger . log (
121+ "[Profiling] Failed to initialize the Profiling constructor, this is likely due to a missing 'Document-Policy': 'js-profiling' header." ,
122+ ) ;
123+ logger . log ( '[Profiling] Disabling profiling for current user session.' ) ;
124+ }
125+ PROFILING_CONSTRUCTOR_FAILED = true ;
126+ }
127+
128+ // We failed to construct the profiler, fallback to original transaction - there is no need to log
129+ // anything as we already did that in the try/catch block.
130+ if ( ! profiler ) {
131+ return transaction ;
132+ }
133+
95134 if ( __DEBUG_BUILD__ ) {
96135 logger . log ( `[Profiling] started profiling transaction: ${ transaction . name || transaction . description } ` ) ;
97136 }
@@ -118,6 +157,10 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
118157 if ( ! transaction ) {
119158 return ;
120159 }
160+ // Satisfy the type checker, but profiler will always be defined here.
161+ if ( ! profiler ) {
162+ return ;
163+ }
121164 if ( processedProfile ) {
122165 if ( __DEBUG_BUILD__ ) {
123166 logger . log (
0 commit comments