Skip to content

Commit 6fc3734

Browse files
committed
feat(tracing): Expose custom browserTracingIntegration
Also migrate angular as an example.
1 parent 10345d4 commit 6fc3734

File tree

10 files changed

+625
-41
lines changed

10 files changed

+625
-41
lines changed

MIGRATION.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,32 @@ npx @sentry/migr8@latest
1010
This will let you select which updates to run, and automatically update your code. Make sure to still review all code
1111
changes!
1212

13-
## Deprecate transaction-related options to `BrowserTracing`
13+
## Deprecate `new BrowserTracing()` in favor of `browserTracingIntegration()`
1414

15-
When configuring the `BrowserTracing` integration, some options have been renamed:
15+
In v8, you have to use the functional style for the browser tracing integration. This works mostly the same, but some of
16+
the options have changed:
1617

17-
- `startTransactionOnPageLoad` --> `spanOnPageLoad`
18-
- `startTransactionOnLocationChange` --> `spanOnLocationChange`
18+
- `startTransactionOnPageLoad` --> `instrumentPageLoad`
19+
- `startTransactionOnLocationChange` --> `instrumentNavigation`
1920
- `markBackgroundTransactions` --> `markBackgroundSpan`
21+
- `beforeNavigate` --> `beforeStartSpan`
2022

21-
Also, `beforeNavigate` is replaced with a `beforeStartSpan` callback, which receives `StartSpanOptions`.
23+
Finally, instead of `routingInstrumentation`, you have to disable instrumentation via e.g.
24+
`instrumentNavigation: false`, and can then manually emit events like this:
25+
26+
```js
27+
// Example router event
28+
router.on('routeChange', route => {
29+
Sentry.getClient().emit('startNavigationSpan', {
30+
name: route.name,
31+
op: 'navigation',
32+
});
33+
34+
const activeSpan = Sentry.getActiveSpan(); // <-- this will hold the navigation span
35+
});
36+
```
37+
38+
The new `browserTracingIntegration()` will pick these up and create the correct spans.
2239

2340
## Deprecate using `getClient()` to check if the SDK was initialized
2441

packages/angular/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,13 @@ Registering a Trace Service is a 3-step process.
9393
instrumentation:
9494

9595
```javascript
96-
import { init, instrumentAngularRouting, BrowserTracing } from '@sentry/angular';
96+
import { init, browserTracingIntegration } from '@sentry/angular';
9797

9898
init({
9999
dsn: '__DSN__',
100100
integrations: [
101-
new BrowserTracing({
101+
browserTracingIntegration({
102102
tracingOrigins: ['localhost', 'https://yourserver.io/api'],
103-
routingInstrumentation: instrumentAngularRouting,
104103
}),
105104
],
106105
tracesSampleRate: 1,

packages/angular/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export {
1010
// TODO `instrumentAngularRouting` is just an alias for `routingInstrumentation`; deprecate the latter at some point
1111
instrumentAngularRouting, // new name
1212
routingInstrumentation, // legacy name
13+
browserTracingIntegration,
1314
TraceClassDecorator,
1415
TraceMethodDecorator,
1516
TraceDirective,

packages/angular/src/tracing.ts

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,19 @@ import type { ActivatedRouteSnapshot, Event, RouterState } from '@angular/router
77
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
88
import { NavigationCancel, NavigationError, Router } from '@angular/router';
99
import { NavigationEnd, NavigationStart, ResolveEnd } from '@angular/router';
10-
import { WINDOW, getCurrentScope } from '@sentry/browser';
11-
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON } from '@sentry/core';
12-
import type { Span, Transaction, TransactionContext } from '@sentry/types';
10+
import {
11+
WINDOW,
12+
browserTracingIntegration as originalBrowserTracingIntegration,
13+
getCurrentScope,
14+
} from '@sentry/browser';
15+
import {
16+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
17+
getActiveSpan,
18+
getClient,
19+
spanToJSON,
20+
startInactiveSpan,
21+
} from '@sentry/core';
22+
import type { Integration, Span, Transaction, TransactionContext } from '@sentry/types';
1323
import { logger, stripUrlQueryAndFragment, timestampInSeconds } from '@sentry/utils';
1424
import type { Observable } from 'rxjs';
1525
import { Subscription } from 'rxjs';
@@ -23,6 +33,8 @@ let instrumentationInitialized: boolean;
2333
let stashedStartTransaction: (context: TransactionContext) => Transaction | undefined;
2434
let stashedStartTransactionOnLocationChange: boolean;
2535

36+
let hooksBasedInstrumentation = false;
37+
2638
/**
2739
* Creates routing instrumentation for Angular Router.
2840
*/
@@ -49,6 +61,23 @@ export function routingInstrumentation(
4961

5062
export const instrumentAngularRouting = routingInstrumentation;
5163

64+
/**
65+
* A custom BrowserTracing integration for Angular.
66+
*/
67+
export function browserTracingIntegration(
68+
options?: Parameters<typeof originalBrowserTracingIntegration>[0],
69+
): Integration {
70+
instrumentationInitialized = true;
71+
hooksBasedInstrumentation = true;
72+
73+
return originalBrowserTracingIntegration({
74+
...options,
75+
instrumentPageLoad: true,
76+
// We handle this manually
77+
instrumentNavigation: false,
78+
});
79+
}
80+
5281
/**
5382
* Grabs active transaction off scope.
5483
*
@@ -74,7 +103,44 @@ export class TraceService implements OnDestroy {
74103
return;
75104
}
76105

106+
if (this._routingSpan) {
107+
this._routingSpan.end();
108+
this._routingSpan = null;
109+
}
110+
77111
const strippedUrl = stripUrlQueryAndFragment(navigationEvent.url);
112+
113+
const client = getClient();
114+
if (hooksBasedInstrumentation && client && client.emit) {
115+
if (!getActiveSpan()) {
116+
client.emit('startNavigationSpan', {
117+
name: strippedUrl,
118+
op: 'navigation',
119+
origin: 'auto.navigation.angular',
120+
attributes: {
121+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
122+
},
123+
});
124+
}
125+
126+
// eslint-disable-next-line deprecation/deprecation
127+
this._routingSpan =
128+
startInactiveSpan({
129+
name: `${navigationEvent.url}`,
130+
op: ANGULAR_ROUTING_OP,
131+
origin: 'auto.ui.angular',
132+
tags: {
133+
'routing.instrumentation': '@sentry/angular',
134+
url: strippedUrl,
135+
...(navigationEvent.navigationTrigger && {
136+
navigationTrigger: navigationEvent.navigationTrigger,
137+
}),
138+
},
139+
}) || null;
140+
141+
return;
142+
}
143+
78144
// eslint-disable-next-line deprecation/deprecation
79145
let activeTransaction = getActiveTransaction();
80146

@@ -90,9 +156,6 @@ export class TraceService implements OnDestroy {
90156
}
91157

92158
if (activeTransaction) {
93-
if (this._routingSpan) {
94-
this._routingSpan.end();
95-
}
96159
// eslint-disable-next-line deprecation/deprecation
97160
this._routingSpan = activeTransaction.startChild({
98161
description: `${navigationEvent.url}`,

packages/core/src/baseclient.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {
2626
SessionAggregates,
2727
Severity,
2828
SeverityLevel,
29+
StartSpanOptions,
2930
Transaction,
3031
TransactionEvent,
3132
Transport,
@@ -481,6 +482,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
481482
callback: (feedback: FeedbackEvent, options?: { includeReplay: boolean }) => void,
482483
): void;
483484

485+
/** @inheritdoc */
486+
public on(hook: 'startPageLoadSpan', callback: (options: StartSpanOptions) => void): void;
487+
488+
/** @inheritdoc */
489+
public on(hook: 'startNavigationSpan', callback: (options: StartSpanOptions) => void): void;
490+
484491
/** @inheritdoc */
485492
public on(hook: string, callback: unknown): void {
486493
if (!this._hooks[hook]) {
@@ -521,6 +528,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
521528
/** @inheritdoc */
522529
public emit(hook: 'beforeSendFeedback', feedback: FeedbackEvent, options?: { includeReplay: boolean }): void;
523530

531+
/** @inheritdoc */
532+
public emit(hook: 'startPageLoadSpan', options: StartSpanOptions): void;
533+
534+
/** @inheritdoc */
535+
public emit(hook: 'startNavigationSpan', options: StartSpanOptions): void;
536+
524537
/** @inheritdoc */
525538
public emit(hook: string, ...rest: unknown[]): void {
526539
if (this._hooks[hook]) {

0 commit comments

Comments
 (0)