@@ -7,9 +7,21 @@ import type { ActivatedRouteSnapshot, Event, RouterState } from '@angular/router
7
7
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
8
8
import { NavigationCancel , NavigationError , Router } from '@angular/router' ;
9
9
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
+ startBrowserTracingNavigationSpan ,
15
+ } from '@sentry/browser' ;
16
+ import {
17
+ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
18
+ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
19
+ getActiveSpan ,
20
+ getClient ,
21
+ spanToJSON ,
22
+ startInactiveSpan ,
23
+ } from '@sentry/core' ;
24
+ import type { Integration , Span , Transaction , TransactionContext } from '@sentry/types' ;
13
25
import { logger , stripUrlQueryAndFragment , timestampInSeconds } from '@sentry/utils' ;
14
26
import type { Observable } from 'rxjs' ;
15
27
import { Subscription } from 'rxjs' ;
@@ -23,8 +35,12 @@ let instrumentationInitialized: boolean;
23
35
let stashedStartTransaction : ( context : TransactionContext ) => Transaction | undefined ;
24
36
let stashedStartTransactionOnLocationChange : boolean ;
25
37
38
+ let hooksBasedInstrumentation = false ;
39
+
26
40
/**
27
41
* Creates routing instrumentation for Angular Router.
42
+ *
43
+ * @deprecated Use `browserTracingIntegration()` instead, which includes Angular-specific instrumentation out of the box.
28
44
*/
29
45
export function routingInstrumentation (
30
46
customStartTransaction : ( context : TransactionContext ) => Transaction | undefined ,
@@ -47,8 +63,35 @@ export function routingInstrumentation(
47
63
}
48
64
}
49
65
66
+ /**
67
+ * Creates routing instrumentation for Angular Router.
68
+ *
69
+ * @deprecated Use `browserTracingIntegration()` instead, which includes Angular-specific instrumentation out of the box.
70
+ */
71
+ // eslint-disable-next-line deprecation/deprecation
50
72
export const instrumentAngularRouting = routingInstrumentation ;
51
73
74
+ /**
75
+ * A custom BrowserTracing integration for Angular.
76
+ *
77
+ * Use this integration in combination with `TraceService`
78
+ */
79
+ export function browserTracingIntegration (
80
+ options : Parameters < typeof originalBrowserTracingIntegration > [ 0 ] = { } ,
81
+ ) : Integration {
82
+ // If the user opts out to set this up, we just don't initialize this.
83
+ // That way, the TraceService will not actually do anything, functionally disabling this.
84
+ if ( options . instrumentNavigation !== false ) {
85
+ instrumentationInitialized = true ;
86
+ hooksBasedInstrumentation = true ;
87
+ }
88
+
89
+ return originalBrowserTracingIntegration ( {
90
+ ...options ,
91
+ instrumentNavigation : false ,
92
+ } ) ;
93
+ }
94
+
52
95
/**
53
96
* Grabs active transaction off scope.
54
97
*
@@ -74,7 +117,44 @@ export class TraceService implements OnDestroy {
74
117
return ;
75
118
}
76
119
120
+ if ( this . _routingSpan ) {
121
+ this . _routingSpan . end ( ) ;
122
+ this . _routingSpan = null ;
123
+ }
124
+
125
+ const client = getClient ( ) ;
77
126
const strippedUrl = stripUrlQueryAndFragment ( navigationEvent . url ) ;
127
+
128
+ if ( client && hooksBasedInstrumentation ) {
129
+ if ( ! getActiveSpan ( ) ) {
130
+ startBrowserTracingNavigationSpan ( client , {
131
+ name : strippedUrl ,
132
+ op : 'navigation' ,
133
+ origin : 'auto.navigation.angular' ,
134
+ attributes : {
135
+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'url' ,
136
+ } ,
137
+ } ) ;
138
+ }
139
+
140
+ // eslint-disable-next-line deprecation/deprecation
141
+ this . _routingSpan =
142
+ startInactiveSpan ( {
143
+ name : `${ navigationEvent . url } ` ,
144
+ op : ANGULAR_ROUTING_OP ,
145
+ origin : 'auto.ui.angular' ,
146
+ tags : {
147
+ 'routing.instrumentation' : '@sentry/angular' ,
148
+ url : strippedUrl ,
149
+ ...( navigationEvent . navigationTrigger && {
150
+ navigationTrigger : navigationEvent . navigationTrigger ,
151
+ } ) ,
152
+ } ,
153
+ } ) || null ;
154
+
155
+ return ;
156
+ }
157
+
78
158
// eslint-disable-next-line deprecation/deprecation
79
159
let activeTransaction = getActiveTransaction ( ) ;
80
160
@@ -90,9 +170,6 @@ export class TraceService implements OnDestroy {
90
170
}
91
171
92
172
if ( activeTransaction ) {
93
- if ( this . _routingSpan ) {
94
- this . _routingSpan . end ( ) ;
95
- }
96
173
// eslint-disable-next-line deprecation/deprecation
97
174
this . _routingSpan = activeTransaction . startChild ( {
98
175
description : `${ navigationEvent . url } ` ,
@@ -132,6 +209,7 @@ export class TraceService implements OnDestroy {
132
209
if ( transaction && attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] === 'url' ) {
133
210
transaction . updateName ( route ) ;
134
211
transaction . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , 'route' ) ;
212
+ transaction . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , `auto.${ spanToJSON ( transaction ) . op } .angular` ) ;
135
213
}
136
214
} ) ,
137
215
) ;
0 commit comments