File tree Expand file tree Collapse file tree 9 files changed +113
-26
lines changed Expand file tree Collapse file tree 9 files changed +113
-26
lines changed Original file line number Diff line number Diff line change @@ -111,9 +111,6 @@ export class IdleTransaction extends Transaction {
111
111
super ( transactionContext , _idleHub ) ;
112
112
113
113
if ( _onScope ) {
114
- // There should only be one active transaction on the scope
115
- clearActiveTransaction ( _idleHub ) ;
116
-
117
114
// We set the transaction here on the scope so error events pick up the trace
118
115
// context and attach it to the error.
119
116
__DEBUG_BUILD__ && logger . log ( `Setting idle transaction on scope. Span ID: ${ this . spanId } ` ) ;
@@ -179,7 +176,10 @@ export class IdleTransaction extends Transaction {
179
176
180
177
// if `this._onScope` is `true`, the transaction put itself on the scope when it started
181
178
if ( this . _onScope ) {
182
- clearActiveTransaction ( this . _idleHub ) ;
179
+ const scope = this . _idleHub . getScope ( ) ;
180
+ if ( scope . getTransaction ( ) === this ) {
181
+ scope . setSpan ( undefined ) ;
182
+ }
183
183
}
184
184
185
185
return super . finish ( endTimestamp ) ;
@@ -353,13 +353,3 @@ export class IdleTransaction extends Transaction {
353
353
} , this . _heartbeatInterval ) ;
354
354
}
355
355
}
356
-
357
- /**
358
- * Reset transaction on scope to `undefined`
359
- */
360
- function clearActiveTransaction ( hub : Hub ) : void {
361
- const scope = hub . getScope ( ) ;
362
- if ( scope . getTransaction ( ) ) {
363
- scope . setSpan ( undefined ) ;
364
- }
365
- }
Original file line number Diff line number Diff line change @@ -3,11 +3,18 @@ import { captureException } from '@sentry/svelte';
3
3
import { addExceptionMechanism , objectify } from '@sentry/utils' ;
4
4
import type { LoadEvent } from '@sveltejs/kit' ;
5
5
6
+ import { isRedirect } from '../common/utils' ;
7
+
6
8
function sendErrorToSentry ( e : unknown ) : unknown {
7
9
// In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can
8
10
// store a seen flag on it.
9
11
const objectifiedErr = objectify ( e ) ;
10
12
13
+ // We don't want to capture thrown `Redirect`s as these are not errors but expected behaviour
14
+ if ( isRedirect ( objectifiedErr ) ) {
15
+ return objectifiedErr ;
16
+ }
17
+
11
18
captureException ( objectifiedErr , scope => {
12
19
scope . addEventProcessor ( event => {
13
20
addExceptionMechanism ( event , {
Original file line number Diff line number Diff line change
1
+ import type { Redirect } from '@sveltejs/kit' ;
2
+
3
+ /**
4
+ * Determines if a thrown "error" is a Redirect object which SvelteKit users can throw to redirect to another route
5
+ * see: https://kit.svelte.dev/docs/modules#sveltejs-kit-redirect
6
+ * @param error the potential redirect error
7
+ */
8
+ export function isRedirect ( error : unknown ) : error is Redirect {
9
+ if ( error == null || typeof error !== 'object' ) {
10
+ return false ;
11
+ }
12
+ const hasValidLocation = 'location' in error && typeof error . location === 'string' ;
13
+ const hasValidStatus =
14
+ 'status' in error && typeof error . status === 'number' && error . status >= 300 && error . status <= 308 ;
15
+ return hasValidLocation && hasValidStatus ;
16
+ }
Original file line number Diff line number Diff line change @@ -5,6 +5,7 @@ import type { TransactionContext } from '@sentry/types';
5
5
import { addExceptionMechanism , objectify } from '@sentry/utils' ;
6
6
import type { HttpError , LoadEvent , ServerLoadEvent } from '@sveltejs/kit' ;
7
7
8
+ import { isRedirect } from '../common/utils' ;
8
9
import { getTracePropagationData } from './utils' ;
9
10
10
11
function isHttpError ( err : unknown ) : err is HttpError {
@@ -19,7 +20,12 @@ function sendErrorToSentry(e: unknown): unknown {
19
20
// The error() helper is commonly used to throw errors in load functions: https://kit.svelte.dev/docs/modules#sveltejs-kit-error
20
21
// If we detect a thrown error that is an instance of HttpError, we don't want to capture 4xx errors as they
21
22
// could be noisy.
22
- if ( isHttpError ( objectifiedErr ) && objectifiedErr . status < 500 && objectifiedErr . status >= 400 ) {
23
+ // Also the `redirect(...)` helper is used to redirect users from one page to another. We don't want to capture thrown
24
+ // `Redirect`s as they're not errors but expected behaviour
25
+ if (
26
+ isRedirect ( objectifiedErr ) ||
27
+ ( isHttpError ( objectifiedErr ) && objectifiedErr . status < 500 && objectifiedErr . status >= 400 )
28
+ ) {
23
29
return objectifiedErr ;
24
30
}
25
31
Original file line number Diff line number Diff line change 1
1
import { addTracingExtensions , Scope } from '@sentry/svelte' ;
2
2
import type { Load } from '@sveltejs/kit' ;
3
+ import { redirect } from '@sveltejs/kit' ;
3
4
import { vi } from 'vitest' ;
4
5
5
6
import { wrapLoadWithSentry } from '../../src/client/load' ;
@@ -99,6 +100,18 @@ describe('wrapLoadWithSentry', () => {
99
100
expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 ) ;
100
101
} ) ;
101
102
103
+ it ( "doesn't call captureException for thrown `Redirect`s" , async ( ) => {
104
+ async function load ( _ : Parameters < Load > [ 0 ] ) : Promise < ReturnType < Load > > {
105
+ throw redirect ( 300 , 'other/route' ) ;
106
+ }
107
+
108
+ const wrappedLoad = wrapLoadWithSentry ( load ) ;
109
+ const res = wrappedLoad ( MOCK_LOAD_ARGS ) ;
110
+ await expect ( res ) . rejects . toThrow ( ) ;
111
+
112
+ expect ( mockCaptureException ) . not . toHaveBeenCalled ( ) ;
113
+ } ) ;
114
+
102
115
it ( 'calls trace function' , async ( ) => {
103
116
async function load ( { params } : Parameters < Load > [ 0 ] ) : Promise < ReturnType < Load > > {
104
117
return {
Original file line number Diff line number Diff line change
1
+ import { isRedirect } from '../../src/common/utils' ;
2
+
3
+ describe ( 'isRedirect' , ( ) => {
4
+ it . each ( [
5
+ { location : '/users/id' , status : 300 } ,
6
+ { location : '/users/id' , status : 304 } ,
7
+ { location : '/users/id' , status : 308 } ,
8
+ { location : '' , status : 308 } ,
9
+ ] ) ( 'returns `true` for valid Redirect objects' , redirectObject => {
10
+ expect ( isRedirect ( redirectObject ) ) . toBe ( true ) ;
11
+ } ) ;
12
+
13
+ it . each ( [
14
+ 300 ,
15
+ 'redirect' ,
16
+ { location : { route : { id : 'users/id' } } , status : 300 } ,
17
+ { status : 308 } ,
18
+ { location : '/users/id' } ,
19
+ { location : '/users/id' , status : 201 } ,
20
+ { location : '/users/id' , status : 400 } ,
21
+ { location : '/users/id' , status : 500 } ,
22
+ ] ) ( 'returns `false` for invalid Redirect objects' , redirectObject => {
23
+ expect ( isRedirect ( redirectObject ) ) . toBe ( false ) ;
24
+ } ) ;
25
+ } ) ;
Original file line number Diff line number Diff line change 1
1
import { addTracingExtensions } from '@sentry/core' ;
2
2
import { Scope } from '@sentry/node' ;
3
3
import type { Load , ServerLoad } from '@sveltejs/kit' ;
4
- import { error } from '@sveltejs/kit' ;
4
+ import { error , redirect } from '@sveltejs/kit' ;
5
5
import { vi } from 'vitest' ;
6
6
7
7
import { wrapLoadWithSentry , wrapServerLoadWithSentry } from '../../src/server/load' ;
@@ -166,6 +166,18 @@ describe.each([
166
166
} ) ;
167
167
} ) ;
168
168
169
+ it ( "doesn't call captureException for thrown `Redirect`s" , async ( ) => {
170
+ async function load ( _ : Parameters < Load > [ 0 ] ) : Promise < ReturnType < Load > > {
171
+ throw redirect ( 300 , 'other/route' ) ;
172
+ }
173
+
174
+ const wrappedLoad = wrapLoadWithSentry ( load ) ;
175
+ const res = wrappedLoad ( MOCK_LOAD_ARGS ) ;
176
+ await expect ( res ) . rejects . toThrow ( ) ;
177
+
178
+ expect ( mockCaptureException ) . not . toHaveBeenCalled ( ) ;
179
+ } ) ;
180
+
169
181
it ( 'adds an exception mechanism' , async ( ) => {
170
182
const addEventProcessorSpy = vi . spyOn ( mockScope , 'addEventProcessor' ) . mockImplementationOnce ( callback => {
171
183
void callback ( { } , { event_id : 'fake-event-id' } ) ;
Original file line number Diff line number Diff line change 1
1
import { BrowserClient } from '@sentry/browser' ;
2
- import { TRACING_DEFAULTS } from '@sentry/core' ;
2
+ import { TRACING_DEFAULTS , Transaction } from '@sentry/core' ;
3
3
4
4
import { Hub , IdleTransaction , Span } from '../../core/src' ;
5
5
import { IdleTransactionSpanRecorder } from '../../core/src/tracing/idletransaction' ;
@@ -75,6 +75,29 @@ describe('IdleTransaction', () => {
75
75
expect ( s . getTransaction ( ) ) . toBe ( undefined ) ;
76
76
} ) ;
77
77
} ) ;
78
+
79
+ it ( 'does not remove transaction from scope on finish if another transaction was set there' , ( ) => {
80
+ const transaction = new IdleTransaction (
81
+ { name : 'foo' } ,
82
+ hub ,
83
+ TRACING_DEFAULTS . idleTimeout ,
84
+ TRACING_DEFAULTS . finalTimeout ,
85
+ TRACING_DEFAULTS . heartbeatInterval ,
86
+ true ,
87
+ ) ;
88
+ transaction . initSpanRecorder ( 10 ) ;
89
+
90
+ // @ts -ignore need to pass in hub
91
+ const otherTransaction = new Transaction ( { name : 'bar' } , hub ) ;
92
+ hub . getScope ( ) . setSpan ( otherTransaction ) ;
93
+
94
+ transaction . finish ( ) ;
95
+ jest . runAllTimers ( ) ;
96
+
97
+ hub . configureScope ( s => {
98
+ expect ( s . getTransaction ( ) ) . toBe ( otherTransaction ) ;
99
+ } ) ;
100
+ } ) ;
78
101
} ) ;
79
102
80
103
beforeEach ( ( ) => {
Original file line number Diff line number Diff line change 4215
4215
highlight.js "^9.15.6"
4216
4216
4217
4217
"@sveltejs/kit@^1.11.0":
4218
- version "1.11.0 "
4219
- resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.11.0 .tgz#23f233c351e5956356ba6f3206e40637c5f5dbda "
4220
- integrity sha512-PwViZcMoLgEU/jhLoSyjf5hSrHS67wvSm0ifBo4prP9irpGa5HuPOZeVDTL5tPDSBoKxtdYi1zlGdoiJfO86jA ==
4218
+ version "1.15.1 "
4219
+ resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.15.1 .tgz#241f1d7e6cf457112b8c098ca26fd2eb85f8db5f "
4220
+ integrity sha512-Wexy3N+COoClTuRawVJRbLoH5HFxNrXG3uoHt/Yd5IGx8WAcJM9Nj/CcBLw/tjCR9uDDYMnx27HxuPy3YIYQUA ==
4221
4221
dependencies:
4222
4222
"@sveltejs/vite-plugin-svelte" "^2.0.0"
4223
4223
"@types/cookie" "^0.5.1"
@@ -23936,12 +23936,7 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
23936
23936
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
23937
23937
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
23938
23938
23939
- set-cookie-parser@^2.4.8:
23940
- version "2.4.8"
23941
- resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2"
23942
- integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==
23943
-
23944
- set-cookie-parser@^2.5.1:
23939
+ set-cookie-parser@^2.4.8, set-cookie-parser@^2.5.1:
23945
23940
version "2.5.1"
23946
23941
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz#ddd3e9a566b0e8e0862aca974a6ac0e01349430b"
23947
23942
integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==
You can’t perform that action at this time.
0 commit comments