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 {
111111 super ( transactionContext , _idleHub ) ;
112112
113113 if ( _onScope ) {
114- // There should only be one active transaction on the scope
115- clearActiveTransaction ( _idleHub ) ;
116-
117114 // We set the transaction here on the scope so error events pick up the trace
118115 // context and attach it to the error.
119116 __DEBUG_BUILD__ && logger . log ( `Setting idle transaction on scope. Span ID: ${ this . spanId } ` ) ;
@@ -179,7 +176,10 @@ export class IdleTransaction extends Transaction {
179176
180177 // if `this._onScope` is `true`, the transaction put itself on the scope when it started
181178 if ( this . _onScope ) {
182- clearActiveTransaction ( this . _idleHub ) ;
179+ const scope = this . _idleHub . getScope ( ) ;
180+ if ( scope . getTransaction ( ) === this ) {
181+ scope . setSpan ( undefined ) ;
182+ }
183183 }
184184
185185 return super . finish ( endTimestamp ) ;
@@ -353,13 +353,3 @@ export class IdleTransaction extends Transaction {
353353 } , this . _heartbeatInterval ) ;
354354 }
355355}
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';
33import { addExceptionMechanism , objectify } from '@sentry/utils' ;
44import type { LoadEvent } from '@sveltejs/kit' ;
55
6+ import { isRedirect } from '../common/utils' ;
7+
68function sendErrorToSentry ( e : unknown ) : unknown {
79 // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can
810 // store a seen flag on it.
911 const objectifiedErr = objectify ( e ) ;
1012
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+
1118 captureException ( objectifiedErr , scope => {
1219 scope . addEventProcessor ( event => {
1320 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';
55import { addExceptionMechanism , objectify } from '@sentry/utils' ;
66import type { HttpError , LoadEvent , ServerLoadEvent } from '@sveltejs/kit' ;
77
8+ import { isRedirect } from '../common/utils' ;
89import { getTracePropagationData } from './utils' ;
910
1011function isHttpError ( err : unknown ) : err is HttpError {
@@ -19,7 +20,12 @@ function sendErrorToSentry(e: unknown): unknown {
1920 // The error() helper is commonly used to throw errors in load functions: https://kit.svelte.dev/docs/modules#sveltejs-kit-error
2021 // If we detect a thrown error that is an instance of HttpError, we don't want to capture 4xx errors as they
2122 // 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+ ) {
2329 return objectifiedErr ;
2430 }
2531
Original file line number Diff line number Diff line change 11import { addTracingExtensions , Scope } from '@sentry/svelte' ;
22import type { Load } from '@sveltejs/kit' ;
3+ import { redirect } from '@sveltejs/kit' ;
34import { vi } from 'vitest' ;
45
56import { wrapLoadWithSentry } from '../../src/client/load' ;
@@ -99,6 +100,18 @@ describe('wrapLoadWithSentry', () => {
99100 expect ( mockCaptureException ) . toHaveBeenCalledTimes ( 1 ) ;
100101 } ) ;
101102
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+
102115 it ( 'calls trace function' , async ( ) => {
103116 async function load ( { params } : Parameters < Load > [ 0 ] ) : Promise < ReturnType < Load > > {
104117 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 11import { addTracingExtensions } from '@sentry/core' ;
22import { Scope } from '@sentry/node' ;
33import type { Load , ServerLoad } from '@sveltejs/kit' ;
4- import { error } from '@sveltejs/kit' ;
4+ import { error , redirect } from '@sveltejs/kit' ;
55import { vi } from 'vitest' ;
66
77import { wrapLoadWithSentry , wrapServerLoadWithSentry } from '../../src/server/load' ;
@@ -166,6 +166,18 @@ describe.each([
166166 } ) ;
167167 } ) ;
168168
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+
169181 it ( 'adds an exception mechanism' , async ( ) => {
170182 const addEventProcessorSpy = vi . spyOn ( mockScope , 'addEventProcessor' ) . mockImplementationOnce ( callback => {
171183 void callback ( { } , { event_id : 'fake-event-id' } ) ;
Original file line number Diff line number Diff line change 11import { BrowserClient } from '@sentry/browser' ;
2- import { TRACING_DEFAULTS } from '@sentry/core' ;
2+ import { TRACING_DEFAULTS , Transaction } from '@sentry/core' ;
33
44import { Hub , IdleTransaction , Span } from '../../core/src' ;
55import { IdleTransactionSpanRecorder } from '../../core/src/tracing/idletransaction' ;
@@ -75,6 +75,29 @@ describe('IdleTransaction', () => {
7575 expect ( s . getTransaction ( ) ) . toBe ( undefined ) ;
7676 } ) ;
7777 } ) ;
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+ } ) ;
78101 } ) ;
79102
80103 beforeEach ( ( ) => {
Original file line number Diff line number Diff line change 42154215 highlight.js "^9.15.6"
42164216
42174217"@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 ==
42214221 dependencies:
42224222 "@sveltejs/vite-plugin-svelte" "^2.0.0"
42234223 "@types/cookie" "^0.5.1"
@@ -23936,12 +23936,7 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
2393623936 resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
2393723937 integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
2393823938
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:
2394523940 version "2.5.1"
2394623941 resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz#ddd3e9a566b0e8e0862aca974a6ac0e01349430b"
2394723942 integrity sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==
You can’t perform that action at this time.
0 commit comments