1
1
import { WINDOW , captureException } from '@sentry/browser' ;
2
- import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , spanToJSON } from '@sentry/core' ;
3
- import type { Transaction , TransactionContext , TransactionSource } from '@sentry/types' ;
2
+ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , spanToJSON } from '@sentry/core' ;
3
+ import type { SpanAttributes , Transaction , TransactionContext , TransactionSource } from '@sentry/types' ;
4
4
5
5
import { getActiveTransaction } from './tracing' ;
6
6
@@ -50,6 +50,8 @@ interface VueRouter {
50
50
* * `routeLabel`: Set this to `route` to opt-out of using `route.name` for transaction names.
51
51
*
52
52
* @param router The Vue Router instance that is used
53
+ *
54
+ * @deprecated Use `browserTracingIntegration()` from `@sentry/vue` instead - this includes the vue router instrumentation.
53
55
*/
54
56
export function vueRouterInstrumentation (
55
57
router : VueRouter ,
@@ -60,88 +62,114 @@ export function vueRouterInstrumentation(
60
62
startTransactionOnPageLoad : boolean = true ,
61
63
startTransactionOnLocationChange : boolean = true ,
62
64
) => {
63
- const tags = {
64
- 'routing.instrumentation' : 'vue-router' ,
65
- } ;
66
-
67
65
// We have to start the pageload transaction as early as possible (before the router's `beforeEach` hook
68
66
// is called) to not miss child spans of the pageload.
69
67
// We check that window & window.location exists in order to not run this code in SSR environments.
70
68
if ( startTransactionOnPageLoad && WINDOW && WINDOW . location ) {
71
69
startTransaction ( {
72
70
name : WINDOW . location . pathname ,
73
71
op : 'pageload' ,
74
- origin : 'auto.pageload.vue' ,
75
- tags,
76
- data : {
72
+ attributes : {
73
+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.pageload.vue' ,
77
74
[ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'url' ,
78
75
} ,
79
76
} ) ;
80
77
}
81
78
82
- router . onError ( error => captureException ( error , { mechanism : { handled : false } } ) ) ;
83
-
84
- router . beforeEach ( ( to , from , next ) => {
85
- // According to docs we could use `from === VueRouter.START_LOCATION` but I couldnt get it working for Vue 2
86
- // https://router.vuejs.org/api/#router-start-location
87
- // https://next.router.vuejs.org/api/#start-location
88
-
89
- // from.name:
90
- // - Vue 2: null
91
- // - Vue 3: undefined
92
- // hence only '==' instead of '===', because `undefined == null` evaluates to `true`
93
- const isPageLoadNavigation = from . name == null && from . matched . length === 0 ;
94
-
95
- const data : Record < string , unknown > = {
96
- params : to . params ,
97
- query : to . query ,
98
- } ;
99
-
100
- // Determine a name for the routing transaction and where that name came from
101
- let transactionName : string = to . path ;
102
- let transactionSource : TransactionSource = 'url' ;
103
- if ( to . name && options . routeLabel !== 'path' ) {
104
- transactionName = to . name . toString ( ) ;
105
- transactionSource = 'custom' ;
106
- } else if ( to . matched [ 0 ] && to . matched [ 0 ] . path ) {
107
- transactionName = to . matched [ 0 ] . path ;
108
- transactionSource = 'route' ;
109
- }
79
+ instrumentVueRouter (
80
+ router ,
81
+ {
82
+ routeLabel : options . routeLabel || 'name' ,
83
+ instrumentNavigation : startTransactionOnLocationChange ,
84
+ instrumentPageLoad : startTransactionOnPageLoad ,
85
+ } ,
86
+ startTransaction ,
87
+ ) ;
88
+ } ;
89
+ }
110
90
111
- if ( startTransactionOnPageLoad && isPageLoadNavigation ) {
112
- // eslint-disable-next-line deprecation/deprecation
113
- const pageloadTransaction = getActiveTransaction ( ) ;
114
- if ( pageloadTransaction ) {
115
- const attributes = spanToJSON ( pageloadTransaction ) . data || { } ;
116
- if ( attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] !== 'custom' ) {
117
- pageloadTransaction . updateName ( transactionName ) ;
118
- pageloadTransaction . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , transactionSource ) ;
119
- }
120
- // TODO: We need to flatten these to make them attributes
121
- // eslint-disable-next-line deprecation/deprecation
122
- pageloadTransaction . setData ( 'params' , data . params ) ;
123
- // eslint-disable-next-line deprecation/deprecation
124
- pageloadTransaction . setData ( 'query' , data . query ) ;
125
- }
91
+ /**
92
+ * Instrument the Vue router to create navigation spans.
93
+ */
94
+ export function instrumentVueRouter (
95
+ router : VueRouter ,
96
+ options : {
97
+ routeLabel : 'name' | 'path' ;
98
+ instrumentPageLoad : boolean ;
99
+ instrumentNavigation : boolean ;
100
+ } ,
101
+ startNavigationSpanFn : ( context : TransactionContext ) => void ,
102
+ ) : void {
103
+ router . onError ( error => captureException ( error , { mechanism : { handled : false } } ) ) ;
104
+
105
+ router . beforeEach ( ( to , from , next ) => {
106
+ // According to docs we could use `from === VueRouter.START_LOCATION` but I couldnt get it working for Vue 2
107
+ // https://router.vuejs.org/api/#router-start-location
108
+ // https://next.router.vuejs.org/api/#start-location
109
+
110
+ // from.name:
111
+ // - Vue 2: null
112
+ // - Vue 3: undefined
113
+ // hence only '==' instead of '===', because `undefined == null` evaluates to `true`
114
+ const isPageLoadNavigation = from . name == null && from . matched . length === 0 ;
115
+
116
+ const attributes : SpanAttributes = {
117
+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.navigation.vue' ,
118
+ } ;
119
+
120
+ for ( const key of Object . keys ( to . params ) ) {
121
+ attributes [ `params.${ key } ` ] = to . params [ key ] ;
122
+ }
123
+ for ( const key of Object . keys ( to . query ) ) {
124
+ const value = to . query [ key ] ;
125
+ if ( value ) {
126
+ attributes [ `query.${ key } ` ] = value ;
126
127
}
128
+ }
127
129
128
- if ( startTransactionOnLocationChange && ! isPageLoadNavigation ) {
129
- data [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] = transactionSource ;
130
- startTransaction ( {
131
- name : transactionName ,
132
- op : 'navigation' ,
133
- origin : 'auto.navigation.vue' ,
134
- tags,
135
- data,
130
+ // Determine a name for the routing transaction and where that name came from
131
+ let transactionName : string = to . path ;
132
+ let transactionSource : TransactionSource = 'url' ;
133
+ if ( to . name && options . routeLabel !== 'path' ) {
134
+ transactionName = to . name . toString ( ) ;
135
+ transactionSource = 'custom' ;
136
+ } else if ( to . matched [ 0 ] && to . matched [ 0 ] . path ) {
137
+ transactionName = to . matched [ 0 ] . path ;
138
+ transactionSource = 'route' ;
139
+ }
140
+
141
+ if ( options . instrumentPageLoad && isPageLoadNavigation ) {
142
+ // eslint-disable-next-line deprecation/deprecation
143
+ const pageloadTransaction = getActiveTransaction ( ) ;
144
+ if ( pageloadTransaction ) {
145
+ const existingAttributes = spanToJSON ( pageloadTransaction ) . data || { } ;
146
+ if ( existingAttributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] !== 'custom' ) {
147
+ pageloadTransaction . updateName ( transactionName ) ;
148
+ pageloadTransaction . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , transactionSource ) ;
149
+ }
150
+ // Set router attributes on the existing pageload transaction
151
+ // This will the origin, and add params & query attributes
152
+ pageloadTransaction . setAttributes ( {
153
+ ...attributes ,
154
+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.pageload.vue' ,
136
155
} ) ;
137
156
}
157
+ }
138
158
139
- // Vue Router 4 no longer exposes the `next` function, so we need to
140
- // check if it's available before calling it.
141
- // `next` needs to be called in Vue Router 3 so that the hook is resolved.
142
- if ( next ) {
143
- next ( ) ;
144
- }
145
- } ) ;
146
- } ;
159
+ if ( options . instrumentNavigation && ! isPageLoadNavigation ) {
160
+ attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] = transactionSource ;
161
+ startNavigationSpanFn ( {
162
+ name : transactionName ,
163
+ op : 'navigation' ,
164
+ attributes,
165
+ } ) ;
166
+ }
167
+
168
+ // Vue Router 4 no longer exposes the `next` function, so we need to
169
+ // check if it's available before calling it.
170
+ // `next` needs to be called in Vue Router 3 so that the hook is resolved.
171
+ if ( next ) {
172
+ next ( ) ;
173
+ }
174
+ } ) ;
147
175
}
0 commit comments