@@ -3,13 +3,25 @@ import { EventProcessor, Integration, Transaction as TransactionType, Transactio
33import { logger } from '@sentry/utils' ;
44
55import { startIdleTransaction } from '../hubextensions' ;
6- import { DEFAULT_IDLE_TIMEOUT } from '../idletransaction' ;
6+ import { DEFAULT_IDLE_TIMEOUT , IdleTransaction } from '../idletransaction' ;
77import { 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' ;
918import { defaultBeforeNavigate , defaultRoutingInstrumentation } from './router' ;
19+ import { secToMs } from './utils' ;
20+
21+ export const DEFAULT_MAX_TRANSACTION_DURATION_SECONDS = 600 ;
1022
1123/** Options for Browser Tracing integration */
12- export interface BrowserTracingOptions {
24+ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
1325 /**
1426 * The time to wait in ms until the transaction will be finished. The transaction will use the end timestamp of
1527 * the last finished span as the endtime for the transaction.
@@ -50,6 +62,24 @@ export interface BrowserTracingOptions {
5062 startTransactionOnPageLoad ?: boolean ,
5163 startTransactionOnLocationChange ?: boolean ,
5264 ) : 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 ;
5383}
5484
5585/**
@@ -69,9 +99,12 @@ export class BrowserTracing implements Integration {
6999 public options : BrowserTracingOptions = {
70100 beforeNavigate : defaultBeforeNavigate ,
71101 idleTimeout : DEFAULT_IDLE_TIMEOUT ,
102+ markBackgroundTransactions : true ,
103+ maxTransactionDuration : DEFAULT_MAX_TRANSACTION_DURATION_SECONDS ,
72104 routingInstrumentation : defaultRoutingInstrumentation ,
73105 startTransactionOnLocationChange : true ,
74106 startTransactionOnPageLoad : true ,
107+ ...defaultRequestInstrumentionOptions ,
75108 } ;
76109
77110 /**
@@ -81,12 +114,28 @@ export class BrowserTracing implements Integration {
81114
82115 private _getCurrentHub ?: ( ) => Hub ;
83116
84- // navigationTransactionInvoker() -> Uses history API NavigationTransaction[]
117+ private readonly _metrics : MetricsInstrumentation = new MetricsInstrumentation ( ) ;
118+
119+ private readonly _emitOptionsWarning : boolean = false ;
85120
86121 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+
87135 this . options = {
88136 ...this . options ,
89137 ..._options ,
138+ tracingOrigins,
90139 } ;
91140 }
92141
@@ -96,13 +145,40 @@ export class BrowserTracing implements Integration {
96145 public setupOnce ( _ : ( callback : EventProcessor ) => void , getCurrentHub : ( ) => Hub ) : void {
97146 this . _getCurrentHub = getCurrentHub ;
98147
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 ;
100167
101168 routingInstrumentation (
102169 ( context : TransactionContext ) => this . _createRouteTransaction ( context ) ,
103170 startTransactionOnPageLoad ,
104171 startTransactionOnLocationChange ,
105172 ) ;
173+
174+ // TODO: Should this be default behaviour?
175+ registerErrorInstrumentation ( ) ;
176+
177+ if ( markBackgroundTransactions ) {
178+ registerBackgroundTabDetection ( ) ;
179+ }
180+
181+ registerRequestInstrumentation ( { traceFetch, traceXHR, tracingOrigins, shouldCreateSpanForRequest } ) ;
106182 }
107183
108184 /** Create routing idle transaction. */
@@ -112,12 +188,13 @@ export class BrowserTracing implements Integration {
112188 return undefined ;
113189 }
114190
115- const { beforeNavigate, idleTimeout } = this . options ;
191+ const { beforeNavigate, idleTimeout, maxTransactionDuration } = this . options ;
116192
117193 // if beforeNavigate returns undefined, we should not start a transaction.
118194 const ctx = beforeNavigate ( {
119195 ...context ,
120196 ...getHeaderContext ( ) ,
197+ trimEnd : true ,
121198 } ) ;
122199
123200 if ( ctx === undefined ) {
@@ -126,8 +203,14 @@ export class BrowserTracing implements Integration {
126203 }
127204
128205 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 ;
131214 }
132215}
133216
@@ -155,3 +238,13 @@ export function getMetaContent(metaName: string): string | null {
155238 const el = document . querySelector ( `meta[name=${ metaName } ]` ) ;
156239 return el ? el . getAttribute ( 'content' ) : null ;
157240}
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