1
1
/* eslint-disable max-lines */
2
2
import { Hub } from '@sentry/core' ;
3
- import { EventProcessor , Integration , Transaction , TransactionContext } from '@sentry/types' ;
3
+ import { EventProcessor , Integration , Transaction , TransactionContext , TransactionSource } from '@sentry/types' ;
4
4
import { baggageHeaderToDynamicSamplingContext , getDomElement , logger } from '@sentry/utils' ;
5
5
6
6
import { startIdleTransaction } from '../hubextensions' ;
7
- import { DEFAULT_FINAL_TIMEOUT , DEFAULT_HEARTBEAT_INTERVAL , DEFAULT_IDLE_TIMEOUT } from '../idletransaction' ;
7
+ import {
8
+ DEFAULT_FINAL_TIMEOUT ,
9
+ DEFAULT_HEARTBEAT_INTERVAL ,
10
+ DEFAULT_IDLE_TIMEOUT ,
11
+ IdleTransaction ,
12
+ } from '../idletransaction' ;
8
13
import { extractTraceparentData } from '../utils' ;
9
14
import { registerBackgroundTabDetection } from './backgroundtab' ;
10
15
import { addPerformanceEntries , startTrackingLongTasks , startTrackingWebVitals } from './metrics' ;
@@ -87,7 +92,7 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
87
92
*
88
93
* Default: undefined
89
94
*/
90
- _experiments ?: Partial < { enableLongTask : boolean } > ;
95
+ _experiments ?: Partial < { enableLongTask : boolean ; interactionSampleRate : number } > ;
91
96
92
97
/**
93
98
* beforeNavigate is called before a pageload/navigation transaction is created and allows users to modify transaction
@@ -120,7 +125,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS = {
120
125
routingInstrumentation : instrumentRoutingWithDefaults ,
121
126
startTransactionOnLocationChange : true ,
122
127
startTransactionOnPageLoad : true ,
123
- _experiments : { enableLongTask : true } ,
128
+ _experiments : { enableLongTask : true , interactionSampleRate : 0.0 } ,
124
129
...defaultRequestInstrumentationOptions ,
125
130
} ;
126
131
@@ -147,6 +152,9 @@ export class BrowserTracing implements Integration {
147
152
148
153
private _getCurrentHub ?: ( ) => Hub ;
149
154
155
+ private _latestRouteName ?: string ;
156
+ private _latestRouteSource ?: TransactionSource ;
157
+
150
158
public constructor ( _options ?: Partial < BrowserTracingOptions > ) {
151
159
this . options = {
152
160
...DEFAULT_BROWSER_TRACING_OPTIONS ,
@@ -177,6 +185,7 @@ export class BrowserTracing implements Integration {
177
185
// eslint-disable-next-line deprecation/deprecation
178
186
tracingOrigins,
179
187
shouldCreateSpanForRequest,
188
+ _experiments,
180
189
} = this . options ;
181
190
182
191
instrumentRouting (
@@ -189,6 +198,10 @@ export class BrowserTracing implements Integration {
189
198
registerBackgroundTabDetection ( ) ;
190
199
}
191
200
201
+ if ( _experiments ?. interactionSampleRate ?? 0 > 0 ) {
202
+ this . _registerInteractionListener ( _experiments ?. interactionSampleRate ?? 0 ) ;
203
+ }
204
+
192
205
instrumentOutgoingRequests ( { traceFetch, traceXHR, tracingOrigins, shouldCreateSpanForRequest } ) ;
193
206
}
194
207
@@ -235,6 +248,9 @@ export class BrowserTracing implements Integration {
235
248
? { ...finalContext . metadata , source : 'custom' }
236
249
: finalContext . metadata ;
237
250
251
+ this . _latestRouteName = finalContext . name ;
252
+ this . _latestRouteSource = finalContext . metadata ?. source ;
253
+
238
254
if ( finalContext . sampled === false ) {
239
255
__DEBUG_BUILD__ &&
240
256
logger . log ( `[Tracing] Will not send ${ finalContext . op } transaction because of beforeNavigate.` ) ;
@@ -264,6 +280,60 @@ export class BrowserTracing implements Integration {
264
280
265
281
return idleTransaction as Transaction ;
266
282
}
283
+
284
+ /** Start listener for interaction transactions */
285
+ private _registerInteractionListener ( interactionSampleRate : number ) : void {
286
+ let inflightInteractionTransaction : IdleTransaction | undefined ;
287
+ const registerInteractionTransaction = ( ) : void => {
288
+ const { idleTimeout, finalTimeout, heartbeatInterval } = this . options ;
289
+ if ( Math . random ( ) > interactionSampleRate ) {
290
+ return ;
291
+ }
292
+
293
+ const op = 'ui.action.click' ;
294
+ if ( inflightInteractionTransaction ) {
295
+ inflightInteractionTransaction . finish ( ) ;
296
+ inflightInteractionTransaction = undefined ;
297
+ }
298
+
299
+ if ( ! this . _getCurrentHub ) {
300
+ __DEBUG_BUILD__ && logger . warn ( `[Tracing] Did not create ${ op } transaction because _getCurrentHub is invalid.` ) ;
301
+ return undefined ;
302
+ }
303
+
304
+ if ( ! this . _latestRouteName ) {
305
+ __DEBUG_BUILD__ &&
306
+ logger . warn ( `[Tracing] Did not create ${ op } transaction because _latestRouteName is missing.` ) ;
307
+ return undefined ;
308
+ }
309
+
310
+ const hub = this . _getCurrentHub ( ) ;
311
+ const { location } = WINDOW ;
312
+
313
+ const context : TransactionContext = {
314
+ name : this . _latestRouteName ,
315
+ op,
316
+ trimEnd : true ,
317
+ metadata : {
318
+ source : this . _latestRouteSource ?? 'url' ,
319
+ } ,
320
+ } ;
321
+
322
+ inflightInteractionTransaction = startIdleTransaction (
323
+ hub ,
324
+ context ,
325
+ idleTimeout ,
326
+ finalTimeout ,
327
+ true ,
328
+ { location } , // for use in the tracesSampler
329
+ heartbeatInterval ,
330
+ ) ;
331
+ } ;
332
+
333
+ [ 'click' ] . forEach ( type => {
334
+ addEventListener ( type , registerInteractionTransaction , { once : false , capture : true } ) ;
335
+ } ) ;
336
+ }
267
337
}
268
338
269
339
/** Returns the value of a meta tag */
0 commit comments