Skip to content

Commit

Permalink
feat(tracing): Expose custom browserTracingIntegration
Browse files Browse the repository at this point in the history
Also migrate angular as an example.
  • Loading branch information
mydea committed Jan 24, 2024
1 parent 0d09983 commit 92ba0ba
Show file tree
Hide file tree
Showing 10 changed files with 730 additions and 23 deletions.
27 changes: 27 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ npx @sentry/migr8@latest
This will let you select which updates to run, and automatically update your code. Make sure to still review all code
changes!

## Deprecate `new BrowserTracing()` in favor of `browserTracingIntegration()`

In v8, you have to use the functional style for the browser tracing integration. This works mostly the same, but some of
the options have changed:

- `startTransactionOnPageLoad` --> `instrumentPageLoad`
- `startTransactionOnLocationChange` --> `instrumentNavigation`
- `markBackgroundTransactions` --> `markBackgroundSpan`
- `beforeNavigate` --> `beforeStartSpan`

Finally, instead of `routingInstrumentation`, you have to disable instrumentation via e.g.
`instrumentNavigation: false`, and can then manually emit events like this:

```js
// Example router event
router.on('routeChange', route => {
Sentry.getClient().emit('startNavigationSpan', {
name: route.name,
op: 'navigation',
});

const activeSpan = Sentry.getActiveSpan(); // <-- this will hold the navigation span
});
```

The new `browserTracingIntegration()` will pick these up and create the correct spans.

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

In v8, `getClient()` will stop returning `undefined` if `Sentry.init()` was not called. For cases where this may be used
Expand Down
5 changes: 2 additions & 3 deletions packages/angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,13 @@ Registering a Trace Service is a 3-step process.
instrumentation:

```javascript
import { init, instrumentAngularRouting, BrowserTracing } from '@sentry/angular';
import { init, browserTracingIntegration } from '@sentry/angular';

init({
dsn: '__DSN__',
integrations: [
new BrowserTracing({
browserTracingIntegration({
tracingOrigins: ['localhost', 'https://yourserver.io/api'],
routingInstrumentation: instrumentAngularRouting,
}),
],
tracesSampleRate: 1,
Expand Down
1 change: 1 addition & 0 deletions packages/angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export {
// TODO `instrumentAngularRouting` is just an alias for `routingInstrumentation`; deprecate the latter at some point
instrumentAngularRouting, // new name
routingInstrumentation, // legacy name
browserTracingIntegration,
TraceClassDecorator,
TraceMethodDecorator,
TraceDirective,
Expand Down
75 changes: 69 additions & 6 deletions packages/angular/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ import type { ActivatedRouteSnapshot, Event, RouterState } from '@angular/router
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import { NavigationCancel, NavigationError, Router } from '@angular/router';
import { NavigationEnd, NavigationStart, ResolveEnd } from '@angular/router';
import { WINDOW, getCurrentScope } from '@sentry/browser';
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, spanToJSON } from '@sentry/core';
import type { Span, Transaction, TransactionContext } from '@sentry/types';
import {
WINDOW,
browserTracingIntegration as originalBrowserTracingIntegration,
getCurrentScope,
} from '@sentry/browser';
import {
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
getActiveSpan,
getClient,
spanToJSON,
startInactiveSpan,
} from '@sentry/core';
import type { Integration, Span, Transaction, TransactionContext } from '@sentry/types';
import { logger, stripUrlQueryAndFragment, timestampInSeconds } from '@sentry/utils';
import type { Observable } from 'rxjs';
import { Subscription } from 'rxjs';
Expand All @@ -23,6 +33,8 @@ let instrumentationInitialized: boolean;
let stashedStartTransaction: (context: TransactionContext) => Transaction | undefined;
let stashedStartTransactionOnLocationChange: boolean;

let hooksBasedInstrumentation = false;

/**
* Creates routing instrumentation for Angular Router.
*/
Expand All @@ -49,6 +61,23 @@ export function routingInstrumentation(

export const instrumentAngularRouting = routingInstrumentation;

/**
* A custom BrowserTracing integration for Angular.
*/
export function browserTracingIntegration(
options?: Parameters<typeof originalBrowserTracingIntegration>[0],
): Integration {
instrumentationInitialized = true;
hooksBasedInstrumentation = true;

return originalBrowserTracingIntegration({
...options,
instrumentPageLoad: true,
// We handle this manually
instrumentNavigation: false,
});
}

/**
* Grabs active transaction off scope.
*
Expand All @@ -74,7 +103,44 @@ export class TraceService implements OnDestroy {
return;
}

if (this._routingSpan) {
this._routingSpan.end();
this._routingSpan = null;
}

const strippedUrl = stripUrlQueryAndFragment(navigationEvent.url);

const client = getClient();
if (hooksBasedInstrumentation && client && client.emit) {
if (!getActiveSpan()) {
client.emit('startNavigationSpan', {
name: strippedUrl,
op: 'navigation',
origin: 'auto.navigation.angular',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
},
});
}

// eslint-disable-next-line deprecation/deprecation
this._routingSpan =
startInactiveSpan({
name: `${navigationEvent.url}`,
op: ANGULAR_ROUTING_OP,
origin: 'auto.ui.angular',
tags: {
'routing.instrumentation': '@sentry/angular',
url: strippedUrl,
...(navigationEvent.navigationTrigger && {
navigationTrigger: navigationEvent.navigationTrigger,
}),
},
}) || null;

return;
}

// eslint-disable-next-line deprecation/deprecation
let activeTransaction = getActiveTransaction();

Expand All @@ -90,9 +156,6 @@ export class TraceService implements OnDestroy {
}

if (activeTransaction) {
if (this._routingSpan) {
this._routingSpan.end();
}
// eslint-disable-next-line deprecation/deprecation
this._routingSpan = activeTransaction.startChild({
description: `${navigationEvent.url}`,
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
SessionAggregates,
Severity,
SeverityLevel,
StartSpanOptions,
Transaction,
TransactionEvent,
Transport,
Expand Down Expand Up @@ -481,6 +482,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
callback: (feedback: FeedbackEvent, options?: { includeReplay: boolean }) => void,
): void;

/** @inheritdoc */
public on(hook: 'startPageLoadSpan', callback: (options: StartSpanOptions) => void): void;

/** @inheritdoc */
public on(hook: 'startNavigationSpan', callback: (options: StartSpanOptions) => void): void;

/** @inheritdoc */
public on(hook: string, callback: unknown): void {
if (!this._hooks[hook]) {
Expand Down Expand Up @@ -521,6 +528,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
/** @inheritdoc */
public emit(hook: 'beforeSendFeedback', feedback: FeedbackEvent, options?: { includeReplay: boolean }): void;

/** @inheritdoc */
public emit(hook: 'startPageLoadSpan', options: StartSpanOptions): void;

/** @inheritdoc */
public emit(hook: 'startNavigationSpan', options: StartSpanOptions): void;

/** @inheritdoc */
public emit(hook: string, ...rest: unknown[]): void {
if (this._hooks[hook]) {
Expand Down
Loading

0 comments on commit 92ba0ba

Please sign in to comment.