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: BrowserTracingOptions = {
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 ,
@@ -185,6 +193,7 @@ export class BrowserTracing implements Integration {
185
193
traceXHR,
186
194
tracePropagationTargets,
187
195
shouldCreateSpanForRequest,
196
+ _experiments,
188
197
} = this . options ;
189
198
190
199
instrumentRouting (
@@ -197,6 +206,10 @@ export class BrowserTracing implements Integration {
197
206
registerBackgroundTabDetection ( ) ;
198
207
}
199
208
209
+ if ( _experiments ?. interactionSampleRate ?? 0 > 0 ) {
210
+ this . _registerInteractionListener ( _experiments ?. interactionSampleRate ?? 0 ) ;
211
+ }
212
+
200
213
instrumentOutgoingRequests ( {
201
214
traceFetch,
202
215
traceXHR,
@@ -248,6 +261,9 @@ export class BrowserTracing implements Integration {
248
261
? { ...finalContext . metadata , source : 'custom' }
249
262
: finalContext . metadata ;
250
263
264
+ this . _latestRouteName = finalContext . name ;
265
+ this . _latestRouteSource = finalContext . metadata ?. source ;
266
+
251
267
if ( finalContext . sampled === false ) {
252
268
__DEBUG_BUILD__ &&
253
269
logger . log ( `[Tracing] Will not send ${ finalContext . op } transaction because of beforeNavigate.` ) ;
@@ -273,6 +289,60 @@ export class BrowserTracing implements Integration {
273
289
274
290
return idleTransaction as Transaction ;
275
291
}
292
+
293
+ /** Start listener for interaction transactions */
294
+ private _registerInteractionListener ( interactionSampleRate : number ) : void {
295
+ let inflightInteractionTransaction : IdleTransaction | undefined ;
296
+ const registerInteractionTransaction = ( ) : void => {
297
+ const { idleTimeout, finalTimeout, heartbeatInterval } = this . options ;
298
+ if ( Math . random ( ) > interactionSampleRate ) {
299
+ return ;
300
+ }
301
+
302
+ const op = 'ui.action.click' ;
303
+ if ( inflightInteractionTransaction ) {
304
+ inflightInteractionTransaction . finish ( ) ;
305
+ inflightInteractionTransaction = undefined ;
306
+ }
307
+
308
+ if ( ! this . _getCurrentHub ) {
309
+ __DEBUG_BUILD__ && logger . warn ( `[Tracing] Did not create ${ op } transaction because _getCurrentHub is invalid.` ) ;
310
+ return undefined ;
311
+ }
312
+
313
+ if ( ! this . _latestRouteName ) {
314
+ __DEBUG_BUILD__ &&
315
+ logger . warn ( `[Tracing] Did not create ${ op } transaction because _latestRouteName is missing.` ) ;
316
+ return undefined ;
317
+ }
318
+
319
+ const hub = this . _getCurrentHub ( ) ;
320
+ const { location } = WINDOW ;
321
+
322
+ const context : TransactionContext = {
323
+ name : this . _latestRouteName ,
324
+ op,
325
+ trimEnd : true ,
326
+ metadata : {
327
+ source : this . _latestRouteSource ?? 'url' ,
328
+ } ,
329
+ } ;
330
+
331
+ inflightInteractionTransaction = startIdleTransaction (
332
+ hub ,
333
+ context ,
334
+ idleTimeout ,
335
+ finalTimeout ,
336
+ true ,
337
+ { location } , // for use in the tracesSampler
338
+ heartbeatInterval ,
339
+ ) ;
340
+ } ;
341
+
342
+ [ 'click' ] . forEach ( type => {
343
+ addEventListener ( type , registerInteractionTransaction , { once : false , capture : true } ) ;
344
+ } ) ;
345
+ }
276
346
}
277
347
278
348
/** Returns the value of a meta tag */
0 commit comments