Skip to content

Commit e3c58c8

Browse files
authored
Merge pull request #17298 from getsentry/prepare-release/10.1.0
meta(changelog): Update changelog for 10.1.0
2 parents f0a938c + 1abe1e7 commit e3c58c8

File tree

20 files changed

+521
-94
lines changed

20 files changed

+521
-94
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
66

7+
## 10.1.0
8+
9+
- feat(nuxt): Align build-time options to follow bundler plugins structure ([#17255](https://github.com/getsentry/sentry-javascript/pull/17255))
10+
- fix(browser-utils): Ensure web vital client hooks unsubscribe correctly ([#17272](https://github.com/getsentry/sentry-javascript/pull/17272))
11+
- fix(browser): Ensure request from `diagnoseSdkConnectivity` doesn't create span ([#17280](https://github.com/getsentry/sentry-javascript/pull/17280))
12+
713
## 10.0.0
814

915
Version `10.0.0` marks a release of the Sentry JavaScript SDKs that contains breaking changes. The goal of this release is to primarily upgrade the underlying OpenTelemetry dependencies to v2 with minimal breaking changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
integrations: [Sentry.browserTracingIntegration({ idleTimeout: 3000, childSpanTimeout: 3000 })],
8+
tracesSampleRate: 1,
9+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sentry.diagnoseSdkConnectivity().then(res => console.log('SDK connectivity:', res));
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../utils/fixtures';
3+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../utils/helpers';
4+
5+
sentryTest('makes a call to sentry.io to diagnose SDK connectivity', async ({ getLocalTestUrl, page }) => {
6+
const bundle = process.env.PW_BUNDLE as string | undefined;
7+
if (shouldSkipTracingTest() || !!bundle) {
8+
// the CDN bundle doesn't export diagnoseSdkConnectivity. So skipping the test for bundles.
9+
sentryTest.skip();
10+
}
11+
12+
const pageloadRequestPromise = waitForTransactionRequest(page, e => e.contexts?.trace?.op === 'pageload');
13+
14+
// mock sdk connectivity url to avoid making actual request to sentry.io
15+
page.route('**/api/4509632503087104/envelope/**/*', route => {
16+
return route.fulfill({
17+
status: 200,
18+
body: '{}',
19+
});
20+
});
21+
22+
const diagnoseMessagePromise = new Promise<string>(resolve => {
23+
page.on('console', msg => {
24+
if (msg.text().includes('SDK connectivity:')) {
25+
resolve(msg.text());
26+
}
27+
});
28+
});
29+
30+
const url = await getLocalTestUrl({ testDir: __dirname });
31+
await page.goto(url);
32+
33+
const pageLoadEvent = envelopeRequestParser(await pageloadRequestPromise);
34+
35+
// undefined is expected and means the request was successful
36+
expect(await diagnoseMessagePromise).toEqual('SDK connectivity: undefined');
37+
38+
// the request to sentry.io should not be traced, hence no http.client span should be sent.
39+
const httpClientSpans = pageLoadEvent.spans?.filter(s => s.op === 'http.client');
40+
expect(httpClientSpans).toHaveLength(0);
41+
42+
// no fetch breadcrumb should be sent (only breadcrumb for the console log)
43+
expect(pageLoadEvent.breadcrumbs).toEqual([
44+
expect.objectContaining({
45+
category: 'console',
46+
message: 'SDK connectivity: undefined',
47+
}),
48+
]);
49+
});

packages/aws-serverless/src/sdk.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,11 @@ export function wrapHandler<TEvent, TResult>(
306306
if (options.captureAllSettledReasons && Array.isArray(rv) && isPromiseAllSettledResult(rv)) {
307307
const reasons = getRejectedReasons(rv);
308308
reasons.forEach(exception => {
309-
captureException(exception, scope => markEventUnhandled(scope));
309+
captureException(exception, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.promise'));
310310
});
311311
}
312312
} catch (e) {
313-
captureException(e, scope => markEventUnhandled(scope));
313+
captureException(e, scope => markEventUnhandled(scope, 'auto.function.aws-serverless.handler'));
314314
throw e;
315315
} finally {
316316
clearTimeout(timeoutWarningTimer);

packages/aws-serverless/src/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ const headerGetter: TextMapGetter<APIGatewayProxyEventHeaders> = {
2626
/**
2727
* Marks an event as unhandled by adding a span processor to the passed scope.
2828
*/
29-
export function markEventUnhandled(scope: Scope): Scope {
29+
export function markEventUnhandled(scope: Scope, type: string): Scope {
3030
scope.addEventProcessor(event => {
31-
addExceptionMechanism(event, { handled: false });
31+
addExceptionMechanism(event, { handled: false, type });
3232
return event;
3333
});
3434

packages/aws-serverless/test/sdk.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ describe('AWSLambda', () => {
501501
// @ts-expect-error just mocking around...
502502
expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({
503503
handled: false,
504-
type: 'generic',
504+
type: 'auto.function.aws-serverless.handler',
505505
});
506506
}
507507
});

packages/browser-utils/src/metrics/utils.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,21 @@ export function listenForWebVitalReportEvents(
226226
// we only want to collect LCP if we actually navigate. Redirects should be ignored.
227227
if (!options?.isRedirect) {
228228
_runCollectorCallbackOnce('navigation');
229-
unsubscribeStartNavigation?.();
230-
unsubscribeAfterStartPageLoadSpan?.();
229+
safeUnsubscribe(unsubscribeStartNavigation, unsubscribeAfterStartPageLoadSpan);
231230
}
232231
});
233232

234233
const unsubscribeAfterStartPageLoadSpan = client.on('afterStartPageLoadSpan', span => {
235234
pageloadSpanId = span.spanContext().spanId;
236-
unsubscribeAfterStartPageLoadSpan?.();
235+
safeUnsubscribe(unsubscribeAfterStartPageLoadSpan);
237236
});
238237
}
238+
239+
/**
240+
* Invoke a list of unsubscribers in a safe way, by deferring the invocation to the next tick.
241+
* This is necessary because unsubscribing in sync can lead to other callbacks no longer being invoked
242+
* due to in-place array mutation of the subscribers array on the client.
243+
*/
244+
function safeUnsubscribe(...unsubscribers: (() => void | undefined)[]): void {
245+
unsubscribers.forEach(u => u && setTimeout(u, 0));
246+
}

packages/browser/src/diagnose-sdk.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getClient } from '@sentry/core';
1+
import { getClient, suppressTracing } from '@sentry/core';
22

33
/**
44
* A function to diagnose why the SDK might not be successfully sending data.
@@ -23,20 +23,22 @@ export async function diagnoseSdkConnectivity(): Promise<
2323
}
2424

2525
try {
26-
// If fetch throws, there is likely an ad blocker active or there are other connective issues.
27-
await fetch(
28-
// We are using the
29-
// - "sentry-sdks" org with id 447951 not to pollute any actual organizations.
30-
// - "diagnose-sdk-connectivity" project with id 4509632503087104
31-
// - the public key of said org/project, which is disabled in the project settings
32-
// => this DSN: https://[email protected]/4509632503087104 (i.e. disabled)
33-
'https://o447951.ingest.sentry.io/api/4509632503087104/envelope/?sentry_version=7&sentry_key=c1dfb07d783ad5325c245c1fd3725390&sentry_client=sentry.javascript.browser%2F1.33.7',
34-
{
35-
body: '{}',
36-
method: 'POST',
37-
mode: 'cors',
38-
credentials: 'omit',
39-
},
26+
await suppressTracing(() =>
27+
// If fetch throws, there is likely an ad blocker active or there are other connective issues.
28+
fetch(
29+
// We are using the
30+
// - "sentry-sdks" org with id 447951 not to pollute any actual organizations.
31+
// - "diagnose-sdk-connectivity" project with id 4509632503087104
32+
// - the public key of said org/project, which is disabled in the project settings
33+
// => this DSN: https://[email protected]/4509632503087104 (i.e. disabled)
34+
'https://o447951.ingest.sentry.io/api/4509632503087104/envelope/?sentry_version=7&sentry_key=c1dfb07d783ad5325c245c1fd3725390&sentry_client=sentry.javascript.browser%2F1.33.7',
35+
{
36+
body: '{}',
37+
method: 'POST',
38+
mode: 'cors',
39+
credentials: 'omit',
40+
},
41+
),
4042
);
4143
} catch {
4244
return 'sentry-unreachable';

packages/browser/test/diagnose-sdk.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,18 @@ describe('diagnoseSdkConnectivity', () => {
162162
credentials: 'omit',
163163
});
164164
});
165+
166+
it('calls suppressTracing to avoid tracing the fetch call to sentry', async () => {
167+
const suppressTracingSpy = vi.spyOn(sentryCore, 'suppressTracing');
168+
169+
const mockClient: Partial<Client> = {
170+
getDsn: vi.fn().mockReturnValue('https://[email protected]/123'),
171+
};
172+
mockGetClient.mockReturnValue(mockClient);
173+
mockFetch.mockResolvedValue(new Response('{}', { status: 200 }));
174+
175+
await diagnoseSdkConnectivity();
176+
177+
expect(suppressTracingSpy).toHaveBeenCalledTimes(1);
178+
});
165179
});

0 commit comments

Comments
 (0)