@@ -3,13 +3,25 @@ import { EventProcessor, Integration, Transaction as TransactionType, Transactio
3
3
import { logger } from '@sentry/utils' ;
4
4
5
5
import { startIdleTransaction } from '../hubextensions' ;
6
- import { DEFAULT_IDLE_TIMEOUT } from '../idletransaction' ;
6
+ import { DEFAULT_IDLE_TIMEOUT , IdleTransaction } from '../idletransaction' ;
7
7
import { Span } from '../span' ;
8
-
8
+ import { SpanStatus } from '../spanstatus' ;
9
+
10
+ import { registerBackgroundTabDetection } from './backgroundtab' ;
11
+ import { registerErrorInstrumentation } from './errors' ;
12
+ import { MetricsInstrumentation } from './metrics' ;
13
+ import {
14
+ defaultRequestInstrumentionOptions ,
15
+ registerRequestInstrumentation ,
16
+ RequestInstrumentationOptions ,
17
+ } from './request' ;
9
18
import { defaultBeforeNavigate , defaultRoutingInstrumentation } from './router' ;
19
+ import { secToMs } from './utils' ;
20
+
21
+ export const DEFAULT_MAX_TRANSACTION_DURATION_SECONDS = 600 ;
10
22
11
23
/** Options for Browser Tracing integration */
12
- export interface BrowserTracingOptions {
24
+ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
13
25
/**
14
26
* The time to wait in ms until the transaction will be finished. The transaction will use the end timestamp of
15
27
* the last finished span as the endtime for the transaction.
@@ -50,6 +62,24 @@ export interface BrowserTracingOptions {
50
62
startTransactionOnPageLoad ?: boolean ,
51
63
startTransactionOnLocationChange ?: boolean ,
52
64
) : void ;
65
+
66
+ /**
67
+ * The maximum duration of a transaction before it will be marked as "deadline_exceeded".
68
+ * If you never want to mark a transaction set it to 0.
69
+ * Time is in seconds.
70
+ *
71
+ * Default: 600
72
+ */
73
+ maxTransactionDuration : number ;
74
+
75
+ /**
76
+ * Flag Transactions where tabs moved to background with "cancelled". Browser background tab timing is
77
+ * not suited towards doing precise measurements of operations. By default, we recommend that this option
78
+ * be enabled as background transactions can mess up your statistics in nondeterministic ways.
79
+ *
80
+ * Default: true
81
+ */
82
+ markBackgroundTransactions : boolean ;
53
83
}
54
84
55
85
/**
@@ -69,9 +99,12 @@ export class BrowserTracing implements Integration {
69
99
public options : BrowserTracingOptions = {
70
100
beforeNavigate : defaultBeforeNavigate ,
71
101
idleTimeout : DEFAULT_IDLE_TIMEOUT ,
102
+ markBackgroundTransactions : true ,
103
+ maxTransactionDuration : DEFAULT_MAX_TRANSACTION_DURATION_SECONDS ,
72
104
routingInstrumentation : defaultRoutingInstrumentation ,
73
105
startTransactionOnLocationChange : true ,
74
106
startTransactionOnPageLoad : true ,
107
+ ...defaultRequestInstrumentionOptions ,
75
108
} ;
76
109
77
110
/**
@@ -81,12 +114,28 @@ export class BrowserTracing implements Integration {
81
114
82
115
private _getCurrentHub ?: ( ) => Hub ;
83
116
84
- // navigationTransactionInvoker() -> Uses history API NavigationTransaction[]
117
+ private readonly _metrics : MetricsInstrumentation = new MetricsInstrumentation ( ) ;
118
+
119
+ private readonly _emitOptionsWarning : boolean = false ;
85
120
86
121
public constructor ( _options ?: Partial < BrowserTracingOptions > ) {
122
+ let tracingOrigins = defaultRequestInstrumentionOptions . tracingOrigins ;
123
+ // NOTE: Logger doesn't work in constructors, as it's initialized after integrations instances
124
+ if (
125
+ _options &&
126
+ _options . tracingOrigins &&
127
+ Array . isArray ( _options . tracingOrigins ) &&
128
+ _options . tracingOrigins . length !== 0
129
+ ) {
130
+ tracingOrigins = _options . tracingOrigins ;
131
+ } else {
132
+ this . _emitOptionsWarning = true ;
133
+ }
134
+
87
135
this . options = {
88
136
...this . options ,
89
137
..._options ,
138
+ tracingOrigins,
90
139
} ;
91
140
}
92
141
@@ -96,13 +145,40 @@ export class BrowserTracing implements Integration {
96
145
public setupOnce ( _ : ( callback : EventProcessor ) => void , getCurrentHub : ( ) => Hub ) : void {
97
146
this . _getCurrentHub = getCurrentHub ;
98
147
99
- const { routingInstrumentation, startTransactionOnLocationChange, startTransactionOnPageLoad } = this . options ;
148
+ if ( this . _emitOptionsWarning ) {
149
+ logger . warn (
150
+ '[Tracing] You need to define `tracingOrigins` in the options. Set an array of urls or patterns to trace.' ,
151
+ ) ;
152
+ logger . warn (
153
+ `[Tracing] We added a reasonable default for you: ${ defaultRequestInstrumentionOptions . tracingOrigins } ` ,
154
+ ) ;
155
+ }
156
+
157
+ const {
158
+ routingInstrumentation,
159
+ startTransactionOnLocationChange,
160
+ startTransactionOnPageLoad,
161
+ markBackgroundTransactions,
162
+ traceFetch,
163
+ traceXHR,
164
+ tracingOrigins,
165
+ shouldCreateSpanForRequest,
166
+ } = this . options ;
100
167
101
168
routingInstrumentation (
102
169
( context : TransactionContext ) => this . _createRouteTransaction ( context ) ,
103
170
startTransactionOnPageLoad ,
104
171
startTransactionOnLocationChange ,
105
172
) ;
173
+
174
+ // TODO: Should this be default behaviour?
175
+ registerErrorInstrumentation ( ) ;
176
+
177
+ if ( markBackgroundTransactions ) {
178
+ registerBackgroundTabDetection ( ) ;
179
+ }
180
+
181
+ registerRequestInstrumentation ( { traceFetch, traceXHR, tracingOrigins, shouldCreateSpanForRequest } ) ;
106
182
}
107
183
108
184
/** Create routing idle transaction. */
@@ -112,12 +188,13 @@ export class BrowserTracing implements Integration {
112
188
return undefined ;
113
189
}
114
190
115
- const { beforeNavigate, idleTimeout } = this . options ;
191
+ const { beforeNavigate, idleTimeout, maxTransactionDuration } = this . options ;
116
192
117
193
// if beforeNavigate returns undefined, we should not start a transaction.
118
194
const ctx = beforeNavigate ( {
119
195
...context ,
120
196
...getHeaderContext ( ) ,
197
+ trimEnd : true ,
121
198
} ) ;
122
199
123
200
if ( ctx === undefined ) {
@@ -126,8 +203,14 @@ export class BrowserTracing implements Integration {
126
203
}
127
204
128
205
const hub = this . _getCurrentHub ( ) ;
129
- logger . log ( `[Tracing] starting ${ ctx . op } idleTransaction on scope with context:` , ctx ) ;
130
- return startIdleTransaction ( hub , ctx , idleTimeout , true ) as TransactionType ;
206
+ logger . log ( `[Tracing] starting ${ ctx . op } idleTransaction on scope` ) ;
207
+ const idleTransaction = startIdleTransaction ( hub , ctx , idleTimeout , true ) ;
208
+ idleTransaction . registerBeforeFinishCallback ( ( transaction , endTimestamp ) => {
209
+ this . _metrics . addPerformanceEntires ( transaction ) ;
210
+ adjustTransactionDuration ( secToMs ( maxTransactionDuration ) , transaction , endTimestamp ) ;
211
+ } ) ;
212
+
213
+ return idleTransaction as TransactionType ;
131
214
}
132
215
}
133
216
@@ -155,3 +238,13 @@ export function getMetaContent(metaName: string): string | null {
155
238
const el = document . querySelector ( `meta[name=${ metaName } ]` ) ;
156
239
return el ? el . getAttribute ( 'content' ) : null ;
157
240
}
241
+
242
+ /** Adjusts transaction value based on max transaction duration */
243
+ function adjustTransactionDuration ( maxDuration : number , transaction : IdleTransaction , endTimestamp : number ) : void {
244
+ const diff = endTimestamp - transaction . startTimestamp ;
245
+ const isOutdatedTransaction = endTimestamp && ( diff > maxDuration || diff < 0 ) ;
246
+ if ( isOutdatedTransaction ) {
247
+ transaction . setStatus ( SpanStatus . DeadlineExceeded ) ;
248
+ transaction . setTag ( 'maxTransactionDurationExceeded' , 'true' ) ;
249
+ }
250
+ }
0 commit comments