1
+ /* eslint-disable max-lines */
1
2
import { Hub } from '@sentry/hub' ;
2
3
import { TransactionContext } from '@sentry/types' ;
3
- import { logger , timestampWithMs } from '@sentry/utils' ;
4
+ import { getGlobalObject , logger , timestampWithMs } from '@sentry/utils' ;
4
5
5
- import { FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS } from './constants' ;
6
6
import { IS_DEBUG_BUILD } from './flags' ;
7
7
import { Span , SpanRecorder } from './span' ;
8
8
import { Transaction } from './transaction' ;
9
9
10
10
export const DEFAULT_IDLE_TIMEOUT = 1000 ;
11
+ export const DEFAULT_FINAL_TIMEOUT = 30000 ;
11
12
export const HEARTBEAT_INTERVAL = 5000 ;
12
13
14
+ const global = getGlobalObject < Window > ( ) ;
15
+
13
16
/**
14
17
* @inheritDoc
15
18
*/
16
19
export class IdleTransactionSpanRecorder extends SpanRecorder {
17
20
public constructor (
18
21
private readonly _pushActivity : ( id : string ) => void ,
19
22
private readonly _popActivity : ( id : string ) => void ,
20
- public transactionSpanId : string = '' ,
23
+ public transactionSpanId : string ,
21
24
maxlen ?: number ,
22
25
) {
23
26
super ( maxlen ) ;
@@ -69,25 +72,27 @@ export class IdleTransaction extends Transaction {
69
72
private readonly _beforeFinishCallbacks : BeforeFinishCallback [ ] = [ ] ;
70
73
71
74
/**
72
- * If a transaction is created and no activities are added, we want to make sure that
73
- * it times out properly. This is cleared and not used when activities are added.
75
+ * Timer that tracks a
74
76
*/
75
- private _initTimeout : ReturnType < typeof setTimeout > | undefined ;
77
+ private _idleTimeoutID : ReturnType < typeof global . setTimeout > | undefined ;
76
78
77
79
public constructor (
78
80
transactionContext : TransactionContext ,
79
- private readonly _idleHub ? : Hub ,
81
+ private readonly _idleHub : Hub ,
80
82
/**
81
83
* The time to wait in ms until the idle transaction will be finished.
82
- * @default 1000
83
84
*/
84
85
private readonly _idleTimeout : number = DEFAULT_IDLE_TIMEOUT ,
86
+ /**
87
+ * The final value in ms that a transaction cannot exceed
88
+ */
89
+ private readonly _finalTimeout : number = DEFAULT_FINAL_TIMEOUT ,
85
90
// Whether or not the transaction should put itself on the scope when it starts and pop itself off when it ends
86
91
private readonly _onScope : boolean = false ,
87
92
) {
88
93
super ( transactionContext , _idleHub ) ;
89
94
90
- if ( _idleHub && _onScope ) {
95
+ if ( _onScope ) {
91
96
// There should only be one active transaction on the scope
92
97
clearActiveTransaction ( _idleHub ) ;
93
98
@@ -97,16 +102,19 @@ export class IdleTransaction extends Transaction {
97
102
_idleHub . configureScope ( scope => scope . setSpan ( this ) ) ;
98
103
}
99
104
100
- this . _initTimeout = setTimeout ( ( ) => {
105
+ this . _startIdleTimeout ( ) ;
106
+ global . setTimeout ( ( ) => {
101
107
if ( ! this . _finished ) {
108
+ this . setStatus ( 'deadline_exceeded' ) ;
102
109
this . finish ( ) ;
103
110
}
104
- } , this . _idleTimeout ) ;
111
+ } , this . _finalTimeout ) ;
105
112
}
106
113
107
114
/** {@inheritDoc } */
108
115
public finish ( endTimestamp : number = timestampWithMs ( ) ) : string | undefined {
109
116
this . _finished = true ;
117
+ this . _cancelIdleTimeout ( ) ;
110
118
this . activities = { } ;
111
119
112
120
if ( this . spanRecorder ) {
@@ -193,15 +201,34 @@ export class IdleTransaction extends Transaction {
193
201
this . spanRecorder . add ( this ) ;
194
202
}
195
203
204
+ /**
205
+ * Cancels the existing idletimeout, if there is one
206
+ */
207
+ private _cancelIdleTimeout ( ) : void {
208
+ if ( this . _idleTimeoutID ) {
209
+ global . clearTimeout ( this . _idleTimeoutID ) ;
210
+ this . _idleTimeoutID = undefined ;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Creates an idletimeout
216
+ */
217
+ private _startIdleTimeout ( endTimestamp ?: Parameters < IdleTransaction [ 'finish' ] > [ 0 ] ) : void {
218
+ this . _cancelIdleTimeout ( ) ;
219
+ this . _idleTimeoutID = global . setTimeout ( ( ) => {
220
+ if ( ! this . _finished && Object . keys ( this . activities ) . length === 0 ) {
221
+ this . finish ( endTimestamp ) ;
222
+ }
223
+ } , this . _idleTimeout ) ;
224
+ }
225
+
196
226
/**
197
227
* Start tracking a specific activity.
198
228
* @param spanId The span id that represents the activity
199
229
*/
200
230
private _pushActivity ( spanId : string ) : void {
201
- if ( this . _initTimeout ) {
202
- clearTimeout ( this . _initTimeout ) ;
203
- this . _initTimeout = undefined ;
204
- }
231
+ this . _cancelIdleTimeout ( ) ;
205
232
IS_DEBUG_BUILD && logger . log ( `[Tracing] pushActivity: ${ spanId } ` ) ;
206
233
this . activities [ spanId ] = true ;
207
234
IS_DEBUG_BUILD && logger . log ( '[Tracing] new activities count' , Object . keys ( this . activities ) . length ) ;
@@ -220,17 +247,10 @@ export class IdleTransaction extends Transaction {
220
247
}
221
248
222
249
if ( Object . keys ( this . activities ) . length === 0 ) {
223
- const timeout = this . _idleTimeout ;
224
250
// We need to add the timeout here to have the real endtimestamp of the transaction
225
251
// Remember timestampWithMs is in seconds, timeout is in ms
226
- const end = timestampWithMs ( ) + timeout / 1000 ;
227
-
228
- setTimeout ( ( ) => {
229
- if ( ! this . _finished ) {
230
- this . setTag ( FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS [ 1 ] ) ;
231
- this . finish ( end ) ;
232
- }
233
- } , timeout ) ;
252
+ const endTimestamp = timestampWithMs ( ) + this . _idleTimeout / 1000 ;
253
+ this . _startIdleTimeout ( endTimestamp ) ;
234
254
}
235
255
}
236
256
@@ -257,7 +277,6 @@ export class IdleTransaction extends Transaction {
257
277
if ( this . _heartbeatCounter >= 3 ) {
258
278
IS_DEBUG_BUILD && logger . log ( '[Tracing] Transaction finished because of no change for 3 heart beats' ) ;
259
279
this . setStatus ( 'deadline_exceeded' ) ;
260
- this . setTag ( FINISH_REASON_TAG , IDLE_TRANSACTION_FINISH_REASONS [ 0 ] ) ;
261
280
this . finish ( ) ;
262
281
} else {
263
282
this . _pingHeartbeat ( ) ;
@@ -269,7 +288,7 @@ export class IdleTransaction extends Transaction {
269
288
*/
270
289
private _pingHeartbeat ( ) : void {
271
290
IS_DEBUG_BUILD && logger . log ( `pinging Heartbeat -> current counter: ${ this . _heartbeatCounter } ` ) ;
272
- setTimeout ( ( ) => {
291
+ global . setTimeout ( ( ) => {
273
292
this . _beat ( ) ;
274
293
} , HEARTBEAT_INTERVAL ) ;
275
294
}
0 commit comments