@@ -3,11 +3,19 @@ import type { CustomSamplingContext, Hub, Transaction, TransactionContext } from
3
3
import { logger , uuid4 } from '@sentry/utils' ;
4
4
5
5
import { 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' ;
7
12
import { sendProfile } from './sendProfile' ;
8
13
9
14
// Max profile duration.
10
15
const 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 ;
11
19
12
20
// While we experiment, per transaction sampling interval will be more flexible to work with.
13
21
type StartTransaction = (
@@ -20,7 +28,7 @@ type StartTransaction = (
20
28
* Check if profiler constructor is available.
21
29
* @param maybeProfiler
22
30
*/
23
- function isJSProfilerSupported ( maybeProfiler : unknown ) : maybeProfiler is typeof JSSelfProfiler {
31
+ function isJSProfilerSupported ( maybeProfiler : unknown ) : maybeProfiler is typeof JSSelfProfilerConstructor {
24
32
return typeof maybeProfiler === 'function' ;
25
33
}
26
34
@@ -49,8 +57,9 @@ export function onProfilingStartRouteTransaction(transaction: Transaction | unde
49
57
*/
50
58
function wrapTransactionWithProfiling ( transaction : Transaction ) : Transaction {
51
59
// Feature support check first
52
- const JSProfiler = WINDOW . Profiler ;
53
- if ( ! isJSProfilerSupported ( JSProfiler ) ) {
60
+ const JSProfilerConstructor = WINDOW . Profiler ;
61
+
62
+ if ( ! isJSProfilerSupported ( JSProfilerConstructor ) ) {
54
63
if ( __DEBUG_BUILD__ ) {
55
64
logger . log (
56
65
'[Profiling] Profiling is not supported by this browser, Profiler interface missing on window object.' ,
@@ -67,6 +76,14 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
67
76
return transaction ;
68
77
}
69
78
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
+
70
87
const client = getCurrentHub ( ) . getClient ( ) ;
71
88
const options = client && client . getOptions ( ) ;
72
89
@@ -91,7 +108,29 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
91
108
const samplingIntervalMS = 10 ;
92
109
// Start the profiler
93
110
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
+
95
134
if ( __DEBUG_BUILD__ ) {
96
135
logger . log ( `[Profiling] started profiling transaction: ${ transaction . name || transaction . description } ` ) ;
97
136
}
@@ -118,6 +157,10 @@ function wrapTransactionWithProfiling(transaction: Transaction): Transaction {
118
157
if ( ! transaction ) {
119
158
return ;
120
159
}
160
+ // Satisfy the type checker, but profiler will always be defined here.
161
+ if ( ! profiler ) {
162
+ return ;
163
+ }
121
164
if ( processedProfile ) {
122
165
if ( __DEBUG_BUILD__ ) {
123
166
logger . log (
0 commit comments