Skip to content

Commit a0394a0

Browse files
Merge remote-tracking branch 'origin/develop' into kw-add-user-feedback-api
2 parents 314b615 + d7021d8 commit a0394a0

File tree

9 files changed

+113
-26
lines changed

9 files changed

+113
-26
lines changed

packages/core/src/tracing/idletransaction.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff 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-
}

packages/sveltekit/src/client/load.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ import { captureException } from '@sentry/svelte';
33
import { addExceptionMechanism, objectify } from '@sentry/utils';
44
import type { LoadEvent } from '@sveltejs/kit';
55

6+
import { isRedirect } from '../common/utils';
7+
68
function 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, {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
}

packages/sveltekit/src/server/load.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { TransactionContext } from '@sentry/types';
55
import { addExceptionMechanism, objectify } from '@sentry/utils';
66
import type { HttpError, LoadEvent, ServerLoadEvent } from '@sveltejs/kit';
77

8+
import { isRedirect } from '../common/utils';
89
import { getTracePropagationData } from './utils';
910

1011
function 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

packages/sveltekit/test/client/load.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { addTracingExtensions, Scope } from '@sentry/svelte';
22
import type { Load } from '@sveltejs/kit';
3+
import { redirect } from '@sveltejs/kit';
34
import { vi } from 'vitest';
45

56
import { 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 {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
});

packages/sveltekit/test/server/load.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { addTracingExtensions } from '@sentry/core';
22
import { Scope } from '@sentry/node';
33
import type { Load, ServerLoad } from '@sveltejs/kit';
4-
import { error } from '@sveltejs/kit';
4+
import { error, redirect } from '@sveltejs/kit';
55
import { vi } from 'vitest';
66

77
import { 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' });

packages/tracing/test/idletransaction.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BrowserClient } from '@sentry/browser';
2-
import { TRACING_DEFAULTS } from '@sentry/core';
2+
import { TRACING_DEFAULTS, Transaction } from '@sentry/core';
33

44
import { Hub, IdleTransaction, Span } from '../../core/src';
55
import { 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(() => {

yarn.lock

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4215,9 +4215,9 @@
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==

0 commit comments

Comments
 (0)