@@ -8,18 +8,13 @@ import type { EmberRunQueues } from '@ember/runloop/-private/types';
8
8
import { getOwnConfig , isTesting , macroCondition } from '@embroider/macros' ;
9
9
import * as Sentry from '@sentry/browser' ;
10
10
import type { ExtendedBackburner } from '@sentry/ember/runloop' ;
11
- import type { Span , Transaction } from '@sentry/types' ;
11
+ import type { Span } from '@sentry/types' ;
12
12
import { GLOBAL_OBJ , browserPerformanceTimeOrigin , timestampInSeconds } from '@sentry/utils' ;
13
13
14
14
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core' ;
15
15
import type { BrowserClient } from '..' ;
16
16
import { getActiveSpan , startInactiveSpan } from '..' ;
17
- import type { EmberRouterMain , EmberSentryConfig , GlobalConfig , OwnConfig , StartTransactionFunction } from '../types' ;
18
-
19
- type SentryTestRouterService = RouterService & {
20
- _startTransaction ?: StartTransactionFunction ;
21
- _sentryInstrumented ?: boolean ;
22
- } ;
17
+ import type { EmberRouterMain , EmberSentryConfig , GlobalConfig , OwnConfig } from '../types' ;
23
18
24
19
function getSentryConfig ( ) : EmberSentryConfig {
25
20
const _global = GLOBAL_OBJ as typeof GLOBAL_OBJ & GlobalConfig ;
@@ -98,26 +93,25 @@ export function _instrumentEmberRouter(
98
93
routerService : RouterService ,
99
94
routerMain : EmberRouterMain ,
100
95
config : EmberSentryConfig ,
101
- startTransaction : StartTransactionFunction ,
102
- startTransactionOnPageLoad ?: boolean ,
103
- ) : {
104
- startTransaction : StartTransactionFunction ;
105
- } {
96
+ ) : void {
106
97
const { disableRunloopPerformance } = config ;
107
98
const location = routerMain . location ;
108
- let activeTransaction : Transaction | undefined ;
99
+ let activeRootSpan : Span | undefined ;
109
100
let transitionSpan : Span | undefined ;
110
101
102
+ // Maintaining backwards compatibility with config.browserTracingOptions, but passing it with Sentry options is preferred.
103
+ const browserTracingOptions = config . browserTracingOptions || config . sentry . browserTracingOptions || { } ;
111
104
const url = getLocationURL ( location ) ;
112
105
113
- if ( macroCondition ( isTesting ( ) ) ) {
114
- ( routerService as SentryTestRouterService ) . _sentryInstrumented = true ;
115
- ( routerService as SentryTestRouterService ) . _startTransaction = startTransaction ;
106
+ const client = Sentry . getClient < BrowserClient > ( ) ;
107
+
108
+ if ( ! client ) {
109
+ return ;
116
110
}
117
111
118
- if ( startTransactionOnPageLoad && url ) {
112
+ if ( url && browserTracingOptions . startTransactionOnPageLoad !== false ) {
119
113
const routeInfo = routerService . recognize ( url ) ;
120
- activeTransaction = startTransaction ( {
114
+ Sentry . startBrowserTracingPageLoadSpan ( client , {
121
115
name : `route:${ routeInfo . name } ` ,
122
116
op : 'pageload' ,
123
117
origin : 'auto.pageload.ember' ,
@@ -127,20 +121,26 @@ export function _instrumentEmberRouter(
127
121
'routing.instrumentation' : '@sentry/ember' ,
128
122
} ,
129
123
} ) ;
124
+ activeRootSpan = getActiveSpan ( ) ;
130
125
}
131
126
132
127
const finishActiveTransaction = ( _ : unknown , nextInstance : unknown ) : void => {
133
128
if ( nextInstance ) {
134
129
return ;
135
130
}
136
- activeTransaction ?. end ( ) ;
131
+ activeRootSpan ?. end ( ) ;
137
132
getBackburner ( ) . off ( 'end' , finishActiveTransaction ) ;
138
133
} ;
139
134
135
+ if ( browserTracingOptions . startTransactionOnLocationChange === false ) {
136
+ return ;
137
+ }
138
+
140
139
routerService . on ( 'routeWillChange' , ( transition : Transition ) => {
141
140
const { fromRoute, toRoute } = getTransitionInformation ( transition , routerService ) ;
142
- activeTransaction ?. end ( ) ;
143
- activeTransaction = startTransaction ( {
141
+ activeRootSpan ?. end ( ) ;
142
+
143
+ Sentry . startBrowserTracingNavigationSpan ( client , {
144
144
name : `route:${ toRoute } ` ,
145
145
op : 'navigation' ,
146
146
origin : 'auto.navigation.ember' ,
@@ -150,6 +150,9 @@ export function _instrumentEmberRouter(
150
150
'routing.instrumentation' : '@sentry/ember' ,
151
151
} ,
152
152
} ) ;
153
+
154
+ activeRootSpan = getActiveSpan ( ) ;
155
+
153
156
transitionSpan = startInactiveSpan ( {
154
157
attributes : {
155
158
[ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.ui.ember' ,
@@ -160,22 +163,18 @@ export function _instrumentEmberRouter(
160
163
} ) ;
161
164
162
165
routerService . on ( 'routeDidChange' , ( ) => {
163
- if ( ! transitionSpan || ! activeTransaction ) {
166
+ if ( ! transitionSpan || ! activeRootSpan ) {
164
167
return ;
165
168
}
166
169
transitionSpan . end ( ) ;
167
170
168
171
if ( disableRunloopPerformance ) {
169
- activeTransaction . end ( ) ;
172
+ activeRootSpan . end ( ) ;
170
173
return ;
171
174
}
172
175
173
176
getBackburner ( ) . on ( 'end' , finishActiveTransaction ) ;
174
177
} ) ;
175
-
176
- return {
177
- startTransaction,
178
- } ;
179
178
}
180
179
181
180
function _instrumentEmberRunloop ( config : EmberSentryConfig ) : void {
@@ -411,61 +410,63 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance)
411
410
// Maintaining backwards compatibility with config.browserTracingOptions, but passing it with Sentry options is preferred.
412
411
const browserTracingOptions = config . browserTracingOptions || config . sentry . browserTracingOptions || { } ;
413
412
414
- const { BrowserTracing } = await import ( '@sentry/browser' ) ;
413
+ const { browserTracingIntegration } = await import ( '@sentry/browser' ) ;
415
414
416
415
const idleTimeout = config . transitionTimeout || 5000 ;
417
416
418
- const browserTracing = new BrowserTracing ( {
419
- routingInstrumentation : ( customStartTransaction , startTransactionOnPageLoad ) => {
420
- // eslint-disable-next-line ember/no-private-routing-service
421
- const routerMain = appInstance . lookup ( 'router:main' ) as EmberRouterMain ;
422
- let routerService = appInstance . lookup ( 'service:router' ) as RouterService & {
423
- externalRouter ?: RouterService ;
424
- _hasMountedSentryPerformanceRouting ?: boolean ;
425
- } ;
426
-
427
- if ( routerService . externalRouter ) {
428
- // Using ember-engines-router-service in an engine.
429
- routerService = routerService . externalRouter ;
430
- }
431
- if ( routerService . _hasMountedSentryPerformanceRouting ) {
432
- // Routing listens to route changes on the main router, and should not be initialized multiple times per page.
433
- return ;
434
- }
435
- if ( ! routerService . recognize ) {
436
- // Router is missing critical functionality to limit cardinality of the transaction names.
437
- return ;
438
- }
439
- routerService . _hasMountedSentryPerformanceRouting = true ;
440
- _instrumentEmberRouter ( routerService , routerMain , config , customStartTransaction , startTransactionOnPageLoad ) ;
441
- } ,
417
+ const browserTracing = browserTracingIntegration ( {
442
418
idleTimeout,
443
419
...browserTracingOptions ,
420
+ instrumentNavigation : false ,
421
+ instrumentPageLoad : false ,
444
422
} ) ;
445
423
446
- if ( macroCondition ( isTesting ( ) ) ) {
447
- const client = Sentry . getClient ( ) ;
448
-
449
- if (
450
- client &&
451
- ( client as BrowserClient ) . getIntegrationByName &&
452
- ( client as BrowserClient ) . getIntegrationByName ( 'BrowserTracing' )
453
- ) {
454
- // Initializers are called more than once in tests, causing the integrations to not be setup correctly.
455
- return ;
456
- }
457
- }
424
+ const client = Sentry . getClient < BrowserClient > ( ) ;
425
+
426
+ const isAlreadyInitialized = macroCondition ( isTesting ( ) ) ? ! ! client ?. getIntegrationByName ( 'BrowserTracing' ) : false ;
458
427
459
- const client = Sentry . getClient ( ) ;
460
428
if ( client && client . addIntegration ) {
461
429
client . addIntegration ( browserTracing ) ;
462
430
}
463
431
432
+ // We _always_ call this, as it triggers the page load & navigation spans
433
+ _instrumentNavigation ( appInstance , config ) ;
434
+
435
+ // Skip instrumenting the stuff below again in tests, as these are not reset between tests
436
+ if ( isAlreadyInitialized ) {
437
+ return ;
438
+ }
439
+
464
440
_instrumentEmberRunloop ( config ) ;
465
441
_instrumentComponents ( config ) ;
466
442
_instrumentInitialLoad ( config ) ;
467
443
}
468
444
445
+ function _instrumentNavigation ( appInstance : ApplicationInstance , config : EmberSentryConfig ) : void {
446
+ // eslint-disable-next-line ember/no-private-routing-service
447
+ const routerMain = appInstance . lookup ( 'router:main' ) as EmberRouterMain ;
448
+ let routerService = appInstance . lookup ( 'service:router' ) as RouterService & {
449
+ externalRouter ?: RouterService ;
450
+ _hasMountedSentryPerformanceRouting ?: boolean ;
451
+ } ;
452
+
453
+ if ( routerService . externalRouter ) {
454
+ // Using ember-engines-router-service in an engine.
455
+ routerService = routerService . externalRouter ;
456
+ }
457
+ if ( routerService . _hasMountedSentryPerformanceRouting ) {
458
+ // Routing listens to route changes on the main router, and should not be initialized multiple times per page.
459
+ return ;
460
+ }
461
+ if ( ! routerService . recognize ) {
462
+ // Router is missing critical functionality to limit cardinality of the transaction names.
463
+ return ;
464
+ }
465
+
466
+ routerService . _hasMountedSentryPerformanceRouting = true ;
467
+ _instrumentEmberRouter ( routerService , routerMain , config ) ;
468
+ }
469
+
469
470
export default {
470
471
initialize,
471
472
} ;
0 commit comments