Skip to content

Commit 6576120

Browse files
authored
Merge branch 'develop' into cg-rr-server-loaders
2 parents 7d367d8 + 8f5f2f3 commit 6576120

File tree

236 files changed

+6278
-1751
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

236 files changed

+6278
-1751
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ jobs:
475475
strategy:
476476
fail-fast: false
477477
matrix:
478-
node: [18, 20, 22]
478+
node: [18, 20, 22, '^24.0.1']
479479
steps:
480480
- name: Check out base commit (${{ github.event.pull_request.base.sha }})
481481
uses: actions/checkout@v4
@@ -713,12 +713,12 @@ jobs:
713713
strategy:
714714
fail-fast: false
715715
matrix:
716-
node: ['18.20.5', 20, 22]
716+
node: ['18.20.5', 20, 22, 24]
717717
typescript:
718718
- false
719719
include:
720720
# Only check typescript for latest version (to streamline CI)
721-
- node: 22
721+
- node: 24
722722
typescript: '3.8'
723723
steps:
724724
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})
@@ -760,7 +760,7 @@ jobs:
760760
strategy:
761761
fail-fast: false
762762
matrix:
763-
node: [18, 20, 22]
763+
node: [18, 20, 22, 24]
764764
steps:
765765
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})
766766
uses: actions/checkout@v4

.size-limit.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module.exports = [
4747
path: 'packages/browser/build/npm/esm/index.js',
4848
import: createImport('init', 'browserTracingIntegration', 'replayIntegration'),
4949
gzip: true,
50-
limit: '76 KB',
50+
limit: '77 KB',
5151
},
5252
{
5353
name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags',
@@ -79,7 +79,7 @@ module.exports = [
7979
path: 'packages/browser/build/npm/esm/index.js',
8080
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'),
8181
gzip: true,
82-
limit: '81 KB',
82+
limit: '82 KB',
8383
},
8484
{
8585
name: '@sentry/browser (incl. Tracing, Replay, Feedback)',
@@ -219,7 +219,7 @@ module.exports = [
219219
import: createImport('init'),
220220
ignore: ['$app/stores'],
221221
gzip: true,
222-
limit: '38.5 KB',
222+
limit: '39 KB',
223223
},
224224
// Node SDK (ESM)
225225
{

CHANGELOG.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,74 @@
1010

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

13+
## 9.17.0
14+
15+
- feat(node): Migrate to `@fastify/otel` ([#15542](https://github.com/getsentry/sentry-javascript/pull/15542))
16+
17+
## 9.16.1
18+
19+
- fix(core): Make sure logs get flushed in server-runtime-client ([#16222](https://github.com/getsentry/sentry-javascript/pull/16222))
20+
- ref(node): Remove vercel flushing code that does nothing ([#16217](https://github.com/getsentry/sentry-javascript/pull/16217))
21+
22+
## 9.16.0
23+
24+
### Important changes
25+
26+
- **feat: Create a Vite plugin that injects sentryConfig into the global config ([#16197](https://github.com/getsentry/sentry-javascript/pull/16197))**
27+
28+
Add a new plugin `makeConfigInjectorPlugin` within our existing vite plugin that updates the global vite config with sentry options
29+
30+
- **feat(browser): Add option to sample linked traces consistently ([#16037](https://github.com/getsentry/sentry-javascript/pull/16037))**
31+
32+
This PR implements consistent sampling across traces as outlined in ([#15754](https://github.com/getsentry/sentry-javascript/pull/15754))
33+
34+
- **feat(cloudflare): Add support for durable objects ([#16180](https://github.com/getsentry/sentry-javascript/pull/16180))**
35+
36+
This PR introduces a new `instrumentDurableObjectWithSentry` method to the SDK, which instruments durable objects. We capture both traces and errors automatically.
37+
38+
- **feat(node): Add Prisma integration by default ([#16073](https://github.com/getsentry/sentry-javascript/pull/16073))**
39+
40+
[Prisma integration](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/prisma/) is enabled by default, it should work for both ESM and CJS.
41+
42+
- **feat(react-router): Add client-side router instrumentation ([#16185](https://github.com/getsentry/sentry-javascript/pull/16185))**
43+
44+
Adds client-side instrumentation for react router's `HydratedRouter`. To enable it, simply replace `browserTracingIntegration()` with `reactRouterTracingIntegration()` in your client-side init call.
45+
46+
- **fix(node): Avoid double-wrapping http module ([#16177](https://github.com/getsentry/sentry-javascript/pull/16177))**
47+
48+
When running your application in ESM mode, there have been scenarios that resulted in the `http`/`https` emitting duplicate spans for incoming requests. This was apparently caused by us double-wrapping the modules for incoming request isolation.
49+
50+
In order to solve this problem, the modules are no longer monkey patched by us for request isolation. Instead, we register diagnostics*channel hooks to handle request isolation now.
51+
While this is generally not expected to break anything, there is one tiny change that \_may* affect you if you have been relying on very specific functionality:
52+
53+
The `ignoreOutgoingRequests` option of `httpIntegration` receives the `RequestOptions` as second argument. This type is not changed, however due to how the wrapping now works, we no longer pass through the full RequestOptions, but re-construct this partially based on the generated request. For the vast majority of cases, this should be fine, but for the sake of completeness, these are the only fields that may be available there going forward - other fields that _may_ have existed before may no longer be set:
54+
55+
```ts
56+
ignoreOutgoingRequests(url: string, {
57+
method: string;
58+
protocol: string;
59+
host: string;
60+
hostname: string; // same as host
61+
path: string;
62+
headers: OutgoingHttpHeaders;
63+
})
64+
```
65+
66+
### Other changes
67+
68+
- feat(cloudflare): Add logs exports ([#16165](https://github.com/getsentry/sentry-javascript/pull/16165))
69+
- feat(vercel-edge): Add logs export ([#16166](https://github.com/getsentry/sentry-javascript/pull/16166))
70+
- feat(cloudflare): Read `SENTRY_RELEASE` from `env` ([#16201](https://github.com/getsentry/sentry-javascript/pull/16201))
71+
- feat(node): Drop `http.server` spans with 404 status by default ([#16205](https://github.com/getsentry/sentry-javascript/pull/16205))
72+
- fix(browser): Respect manually set sentry tracing headers in XHR requests ([#16184](https://github.com/getsentry/sentry-javascript/pull/16184))
73+
- fix(core): Respect manually set sentry tracing headers in fetch calls ([#16183](https://github.com/getsentry/sentry-javascript/pull/16183))
74+
- fix(feedback): Prevent `removeFromDom()` from throwing ([#16030](https://github.com/getsentry/sentry-javascript/pull/16030))
75+
- fix(node): Use class constructor in docstring for winston transport ([#16167](https://github.com/getsentry/sentry-javascript/pull/16167))
76+
- fix(node): Fix vercel flushing logic & add test for it ([#16208](https://github.com/getsentry/sentry-javascript/pull/16208))
77+
- fix(node): Fix 404 route handling in express 5 ([#16211](https://github.com/getsentry/sentry-javascript/pull/16211))
78+
- fix(logs): Ensure logs can be flushed correctly ([#16216](https://github.com/getsentry/sentry-javascript/pull/16216))
79+
- ref(core): Switch to standardized log envelope ([#16133](https://github.com/getsentry/sentry-javascript/pull/16133))
80+
1381
## 9.15.0
1482

1583
### Important Changes

dev-packages/browser-integration-tests/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sentry-internal/browser-integration-tests",
3-
"version": "9.15.0",
3+
"version": "9.17.0",
44
"main": "index.js",
55
"license": "MIT",
66
"engines": {
@@ -42,7 +42,7 @@
4242
"@babel/preset-typescript": "^7.16.7",
4343
"@playwright/test": "~1.50.0",
4444
"@sentry-internal/rrweb": "2.34.0",
45-
"@sentry/browser": "9.15.0",
45+
"@sentry/browser": "9.17.0",
4646
"@supabase/supabase-js": "2.49.3",
4747
"axios": "1.8.2",
4848
"babel-loader": "^8.2.2",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
integrations: [
8+
Sentry.browserTracingIntegration({
9+
linkPreviousTrace: 'in-memory',
10+
consistentTraceSampling: true,
11+
}),
12+
],
13+
tracePropagationTargets: ['someurl.com'],
14+
tracesSampler: ctx => {
15+
if (ctx.attributes && ctx.attributes['sentry.origin'] === 'auto.pageload.browser') {
16+
return 1;
17+
}
18+
return ctx.inheritOrSampleWith(0);
19+
},
20+
debug: true,
21+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const btn1 = document.getElementById('btn1');
2+
3+
const btn2 = document.getElementById('btn2');
4+
5+
btn1.addEventListener('click', () => {
6+
Sentry.startNewTrace(() => {
7+
Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {});
8+
});
9+
});
10+
11+
btn2.addEventListener('click', () => {
12+
Sentry.startNewTrace(() => {
13+
Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => {
14+
await fetch('https://someUrl.com');
15+
});
16+
});
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<button id="btn1">
7+
</button>
8+
<button id="btn2">
9+
</button>
10+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
extractTraceparentData,
4+
parseBaggageHeader,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE,
7+
} from '@sentry/core';
8+
import { sentryTest } from '../../../../../../utils/fixtures';
9+
import {
10+
eventAndTraceHeaderRequestParser,
11+
shouldSkipTracingTest,
12+
waitForTracingHeadersOnUrl,
13+
waitForTransactionRequest,
14+
} from '../../../../../../utils/helpers';
15+
16+
sentryTest.describe('When `consistentTraceSampling` is `true`', () => {
17+
sentryTest('Continues sampling decision from initial pageload', async ({ getLocalTestUrl, page }) => {
18+
if (shouldSkipTracingTest()) {
19+
sentryTest.skip();
20+
}
21+
22+
const url = await getLocalTestUrl({ testDir: __dirname });
23+
24+
const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => {
25+
const pageloadRequestPromise = waitForTransactionRequest(page, evt => {
26+
return evt.contexts?.trace?.op === 'pageload';
27+
});
28+
await page.goto(url);
29+
30+
const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise);
31+
const pageloadSampleRand = Number(res[1]?.sample_rand);
32+
const pageloadTraceContext = res[0].contexts?.trace;
33+
34+
expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1);
35+
expect(pageloadSampleRand).toBeGreaterThanOrEqual(0);
36+
37+
return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand };
38+
});
39+
40+
const customTraceContext = await sentryTest.step('Custom trace', async () => {
41+
const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom');
42+
await page.locator('#btn1').click();
43+
const [customTrace1Event, customTraceTraceHeader] = eventAndTraceHeaderRequestParser(
44+
await customTrace1RequestPromise,
45+
);
46+
47+
const customTraceContext = customTrace1Event.contexts?.trace;
48+
49+
expect(customTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id);
50+
// although we "continue the trace" from pageload, this is actually a root span,
51+
// so there must not be a parent span id
52+
expect(customTraceContext?.parent_span_id).toBeUndefined();
53+
54+
expect(pageloadSampleRand).toEqual(Number(customTraceTraceHeader?.sample_rand));
55+
56+
return customTraceContext;
57+
});
58+
59+
await sentryTest.step('Navigation', async () => {
60+
const navigation1RequestPromise = waitForTransactionRequest(
61+
page,
62+
evt => evt.contexts?.trace?.op === 'navigation',
63+
);
64+
await page.goto(`${url}#foo`);
65+
const [navigationEvent, navigationTraceHeader] = eventAndTraceHeaderRequestParser(
66+
await navigation1RequestPromise,
67+
);
68+
const navTraceContext = navigationEvent.contexts?.trace;
69+
70+
expect(navTraceContext?.trace_id).not.toEqual(customTraceContext?.trace_id);
71+
expect(navTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id);
72+
73+
expect(navTraceContext?.links).toEqual([
74+
{
75+
trace_id: customTraceContext?.trace_id,
76+
span_id: customTraceContext?.span_id,
77+
sampled: true,
78+
attributes: {
79+
[SEMANTIC_LINK_ATTRIBUTE_LINK_TYPE]: 'previous_trace',
80+
},
81+
},
82+
]);
83+
expect(navTraceContext?.parent_span_id).toBeUndefined();
84+
85+
expect(pageloadSampleRand).toEqual(Number(navigationTraceHeader?.sample_rand));
86+
});
87+
});
88+
89+
sentryTest('Propagates continued sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => {
90+
if (shouldSkipTracingTest()) {
91+
sentryTest.skip();
92+
}
93+
94+
const url = await getLocalTestUrl({ testDir: __dirname });
95+
96+
const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => {
97+
const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload');
98+
await page.goto(url);
99+
100+
const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise);
101+
const pageloadSampleRand = Number(res[1]?.sample_rand);
102+
103+
expect(pageloadSampleRand).toBeGreaterThanOrEqual(0);
104+
105+
const pageloadTraceContext = res[0].contexts?.trace;
106+
107+
expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1);
108+
109+
return { pageloadTraceContext: pageloadTraceContext, pageloadSampleRand };
110+
});
111+
112+
await sentryTest.step('Make fetch request', async () => {
113+
const fetchTracePromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom');
114+
const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'https://someUrl.com');
115+
116+
await page.locator('#btn2').click();
117+
118+
const { baggage, sentryTrace } = await tracingHeadersPromise;
119+
120+
const [fetchTraceEvent, fetchTraceTraceHeader] = eventAndTraceHeaderRequestParser(await fetchTracePromise);
121+
122+
const fetchTraceSampleRand = Number(fetchTraceTraceHeader?.sample_rand);
123+
const fetchTraceTraceContext = fetchTraceEvent.contexts?.trace;
124+
const httpClientSpan = fetchTraceEvent.spans?.find(span => span.op === 'http.client');
125+
126+
expect(fetchTraceSampleRand).toEqual(pageloadSampleRand);
127+
128+
expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toEqual(
129+
pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE],
130+
);
131+
expect(fetchTraceTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id);
132+
133+
expect(sentryTrace).toBeDefined();
134+
expect(baggage).toBeDefined();
135+
136+
expect(extractTraceparentData(sentryTrace)).toEqual({
137+
traceId: fetchTraceTraceContext?.trace_id,
138+
parentSpanId: httpClientSpan?.span_id,
139+
parentSampled: true,
140+
});
141+
142+
expect(parseBaggageHeader(baggage)).toEqual({
143+
'sentry-environment': 'production',
144+
'sentry-public_key': 'public',
145+
'sentry-sample_rand': `${pageloadSampleRand}`,
146+
'sentry-sample_rate': '1',
147+
'sentry-sampled': 'true',
148+
'sentry-trace_id': fetchTraceTraceContext?.trace_id,
149+
'sentry-transaction': 'custom root span 2',
150+
});
151+
});
152+
});
153+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
integrations: [
8+
Sentry.browserTracingIntegration({
9+
linkPreviousTrace: 'in-memory',
10+
consistentTraceSampling: true,
11+
}),
12+
],
13+
tracePropagationTargets: ['someurl.com'],
14+
tracesSampleRate: 1,
15+
debug: true,
16+
sendClientReports: true,
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const btn1 = document.getElementById('btn1');
2+
3+
const btn2 = document.getElementById('btn2');
4+
5+
btn1.addEventListener('click', () => {
6+
Sentry.startNewTrace(() => {
7+
Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {});
8+
});
9+
});
10+
11+
btn2.addEventListener('click', () => {
12+
Sentry.startNewTrace(() => {
13+
Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => {
14+
await fetch('https://someUrl.com');
15+
});
16+
});
17+
});

0 commit comments

Comments
 (0)