@@ -7,9 +7,19 @@ import type { ActivatedRouteSnapshot, Event, RouterState } from '@angular/router
77// eslint-disable-next-line @typescript-eslint/consistent-type-imports
88import { NavigationCancel , NavigationError , Router } from '@angular/router' ;
99import { 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' ;
1323import { logger , stripUrlQueryAndFragment , timestampInSeconds } from '@sentry/utils' ;
1424import type { Observable } from 'rxjs' ;
1525import { Subscription } from 'rxjs' ;
@@ -23,6 +33,8 @@ let instrumentationInitialized: boolean;
2333let stashedStartTransaction : ( context : TransactionContext ) => Transaction | undefined ;
2434let 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
5062export 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 } ` ,
0 commit comments