Skip to content

Commit 87b4bf0

Browse files
committed
ref(ember): Use new browserTracingIntegration() under the hood
1 parent 4097c4a commit 87b4bf0

File tree

4 files changed

+60
-92
lines changed

4 files changed

+60
-92
lines changed

packages/ember/addon/instance-initializers/sentry-performance.ts

Lines changed: 57 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,13 @@ import type { EmberRunQueues } from '@ember/runloop/-private/types';
88
import { getOwnConfig, isTesting, macroCondition } from '@embroider/macros';
99
import * as Sentry from '@sentry/browser';
1010
import type { ExtendedBackburner } from '@sentry/ember/runloop';
11-
import type { Span, Transaction } from '@sentry/types';
11+
import type { Span } from '@sentry/types';
1212
import { GLOBAL_OBJ, browserPerformanceTimeOrigin, timestampInSeconds } from '@sentry/utils';
1313

1414
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
1515
import type { BrowserClient } from '..';
1616
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';
2318

2419
function getSentryConfig(): EmberSentryConfig {
2520
const _global = GLOBAL_OBJ as typeof GLOBAL_OBJ & GlobalConfig;
@@ -98,26 +93,17 @@ export function _instrumentEmberRouter(
9893
routerService: RouterService,
9994
routerMain: EmberRouterMain,
10095
config: EmberSentryConfig,
101-
startTransaction: StartTransactionFunction,
102-
startTransactionOnPageLoad?: boolean,
103-
): {
104-
startTransaction: StartTransactionFunction;
105-
} {
96+
): void {
10697
const { disableRunloopPerformance } = config;
10798
const location = routerMain.location;
108-
let activeTransaction: Transaction | undefined;
99+
let activeRootSpan: Span | undefined;
109100
let transitionSpan: Span | undefined;
110101

111102
const url = getLocationURL(location);
112103

113-
if (macroCondition(isTesting())) {
114-
(routerService as SentryTestRouterService)._sentryInstrumented = true;
115-
(routerService as SentryTestRouterService)._startTransaction = startTransaction;
116-
}
117-
118-
if (startTransactionOnPageLoad && url) {
104+
if (url) {
119105
const routeInfo = routerService.recognize(url);
120-
activeTransaction = startTransaction({
106+
Sentry.startBrowserTracingNavigationSpan({
121107
name: `route:${routeInfo.name}`,
122108
op: 'pageload',
123109
origin: 'auto.pageload.ember',
@@ -127,20 +113,22 @@ export function _instrumentEmberRouter(
127113
'routing.instrumentation': '@sentry/ember',
128114
},
129115
});
116+
activeRootSpan = getActiveSpan();
130117
}
131118

132119
const finishActiveTransaction = (_: unknown, nextInstance: unknown): void => {
133120
if (nextInstance) {
134121
return;
135122
}
136-
activeTransaction?.end();
123+
activeRootSpan?.end();
137124
getBackburner().off('end', finishActiveTransaction);
138125
};
139126

140127
routerService.on('routeWillChange', (transition: Transition) => {
141128
const { fromRoute, toRoute } = getTransitionInformation(transition, routerService);
142-
activeTransaction?.end();
143-
activeTransaction = startTransaction({
129+
activeRootSpan?.end();
130+
131+
Sentry.startBrowserTracingNavigationSpan({
144132
name: `route:${toRoute}`,
145133
op: 'navigation',
146134
origin: 'auto.navigation.ember',
@@ -150,6 +138,9 @@ export function _instrumentEmberRouter(
150138
'routing.instrumentation': '@sentry/ember',
151139
},
152140
});
141+
142+
activeRootSpan = getActiveSpan();
143+
153144
transitionSpan = startInactiveSpan({
154145
attributes: {
155146
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.ember',
@@ -160,22 +151,18 @@ export function _instrumentEmberRouter(
160151
});
161152

162153
routerService.on('routeDidChange', () => {
163-
if (!transitionSpan || !activeTransaction) {
154+
if (!transitionSpan || !activeRootSpan) {
164155
return;
165156
}
166157
transitionSpan.end();
167158

168159
if (disableRunloopPerformance) {
169-
activeTransaction.end();
160+
activeRootSpan.end();
170161
return;
171162
}
172163

173164
getBackburner().on('end', finishActiveTransaction);
174165
});
175-
176-
return {
177-
startTransaction,
178-
};
179166
}
180167

181168
function _instrumentEmberRunloop(config: EmberSentryConfig): void {
@@ -411,61 +398,64 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance)
411398
// Maintaining backwards compatibility with config.browserTracingOptions, but passing it with Sentry options is preferred.
412399
const browserTracingOptions = config.browserTracingOptions || config.sentry.browserTracingOptions || {};
413400

414-
const { BrowserTracing } = await import('@sentry/browser');
401+
const { browserTracingIntegration } = await import('@sentry/browser');
415402

416403
const idleTimeout = config.transitionTimeout || 5000;
417404

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-
},
405+
const browserTracing = browserTracingIntegration({
442406
idleTimeout,
443407
...browserTracingOptions,
444408
});
445409

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-
}
410+
Sentry.disableDefaultBrowserTracingNavigationSpan();
411+
Sentry.disableDefaultBrowserTracingPageLoadSpan();
412+
413+
const client = Sentry.getClient<BrowserClient>();
414+
415+
const isAlreadyInitialized = macroCondition(isTesting()) ? !!client?.getIntegrationByName('BrowserTracing') : false;
458416

459-
const client = Sentry.getClient();
460417
if (client && client.addIntegration) {
461418
client.addIntegration(browserTracing);
462419
}
463420

421+
// We _always_ call this, as it triggers the page load & navigation spans
422+
_instrumentNavigation(appInstance, config);
423+
424+
// Skip instrumenting the stuff below again in tests, as these are not reset between tests
425+
if (isAlreadyInitialized) {
426+
return;
427+
}
428+
464429
_instrumentEmberRunloop(config);
465430
_instrumentComponents(config);
466431
_instrumentInitialLoad(config);
467432
}
468433

434+
function _instrumentNavigation(appInstance: ApplicationInstance, config: EmberSentryConfig): void {
435+
// eslint-disable-next-line ember/no-private-routing-service
436+
const routerMain = appInstance.lookup('router:main') as EmberRouterMain;
437+
let routerService = appInstance.lookup('service:router') as RouterService & {
438+
externalRouter?: RouterService;
439+
_hasMountedSentryPerformanceRouting?: boolean;
440+
};
441+
442+
if (routerService.externalRouter) {
443+
// Using ember-engines-router-service in an engine.
444+
routerService = routerService.externalRouter;
445+
}
446+
if (routerService._hasMountedSentryPerformanceRouting) {
447+
// Routing listens to route changes on the main router, and should not be initialized multiple times per page.
448+
return;
449+
}
450+
if (!routerService.recognize) {
451+
// Router is missing critical functionality to limit cardinality of the transaction names.
452+
return;
453+
}
454+
455+
routerService._hasMountedSentryPerformanceRouting = true;
456+
_instrumentEmberRouter(routerService, routerMain, config);
457+
}
458+
469459
export default {
470460
initialize,
471461
};

packages/ember/addon/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface EmberRouterMain {
3131
rootURL: string;
3232
};
3333
}
34-
34+
/** @deprecated This will be removed in v8. */
3535
export type StartTransactionFunction = (context: TransactionContext) => Transaction | undefined;
3636

3737
export type GlobalConfig = {

packages/ember/tests/helpers/setup-sentry.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,21 @@
1-
import type RouterService from '@ember/routing/router-service';
21
import type { TestContext } from '@ember/test-helpers';
32
import { resetOnerror, setupOnerror } from '@ember/test-helpers';
4-
import { _instrumentEmberRouter } from '@sentry/ember/instance-initializers/sentry-performance';
5-
import type { EmberRouterMain, EmberSentryConfig, StartTransactionFunction } from '@sentry/ember/types';
63
import sinon from 'sinon';
74

8-
// Keep a reference to the original startTransaction as the application gets re-initialized and setup for
9-
// the integration doesn't occur again after the first time.
10-
let _routerStartTransaction: StartTransactionFunction | undefined;
11-
125
export type SentryTestContext = TestContext & {
136
errorMessages: string[];
147
fetchStub: sinon.SinonStub;
158
qunitOnUnhandledRejection: sinon.SinonStub;
169
_windowOnError: OnErrorEventHandler;
1710
};
1811

19-
type SentryRouterService = RouterService & {
20-
_startTransaction: StartTransactionFunction;
21-
_sentryInstrumented?: boolean;
22-
};
23-
2412
export function setupSentryTest(hooks: NestedHooks): void {
2513
hooks.beforeEach(async function (this: SentryTestContext) {
2614
await window._sentryPerformanceLoad;
2715
window._sentryTestEvents = [];
2816
const errorMessages: string[] = [];
2917
this.errorMessages = errorMessages;
3018

31-
// eslint-disable-next-line ember/no-private-routing-service
32-
const routerMain = this.owner.lookup('router:main') as EmberRouterMain;
33-
const routerService = this.owner.lookup('service:router') as SentryRouterService;
34-
35-
if (routerService._sentryInstrumented) {
36-
_routerStartTransaction = routerService._startTransaction;
37-
} else if (_routerStartTransaction) {
38-
_instrumentEmberRouter(routerService, routerMain, {} as EmberSentryConfig, _routerStartTransaction);
39-
}
40-
4119
/**
4220
* Stub out fetch function to assert on Sentry calls.
4321
*/

packages/ember/tests/helpers/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export function assertSentryTransactions(
5858
const sentryTestEvents = getTestSentryTransactions();
5959
const event = sentryTestEvents[callNumber];
6060

61-
assert.ok(event);
62-
assert.ok(event.spans);
61+
assert.ok(event, 'event exists');
62+
assert.ok(event.spans, 'event has spans');
6363

6464
const spans = event.spans || [];
6565

0 commit comments

Comments
 (0)