Skip to content

Commit 6eb8690

Browse files
authored
Merge branch 'develop' into instrument-genai-google-stream
2 parents 6c7272f + 34f3479 commit 6eb8690

File tree

49 files changed

+1098
-634
lines changed

Some content is hidden

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

49 files changed

+1098
-634
lines changed

CHANGELOG.md

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

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

7+
Work in this release was contributed by @Olexandr88. Thank you for your contribution!
8+
9+
### Important Changes
10+
11+
- **feat(browser): Add option to explicitly end pageload span via `reportPageLoaded()` ([#17697](https://github.com/getsentry/sentry-javascript/issues/17212))**
12+
13+
With this release you can take manual control of ending the pageload span. Usually this span is ended automatically by the SDK, based on a period of inactivity after the initial page was loaded in the browser. If you want full control over the pageload duration, you can tell Sentry, when your page was fully loaded:
14+
15+
```js
16+
Sentry.init({
17+
//...
18+
integrations: [
19+
// 1. Enable manual pageload reporting
20+
Sentry.browserTracingIntegration({ enableReportPageLoaded: true }),
21+
],
22+
});
23+
24+
// 2. Whenever you decide the page is loaded, call:
25+
Sentry.reportPageLoaded();
26+
```
27+
28+
Note that if `Sentry.reportPageLoaded()` is not called within 30 seconds of the initial pageload (or whatever value the `finalTimeout` option is set to), the pageload span will be ended automatically.
29+
730
## 10.12.0
831

932
### Important Changes

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ _Bad software is everywhere, and we're tired of it. Sentry is on a mission to he
88
faster, so we can get back to enjoying technology. If you want to join us
99
[<kbd>**Check out our open positions**</kbd>](https://sentry.io/careers/)_
1010

11-
![Build & Test](https://github.com/getsentry/sentry-javascript/workflows/CI:%20Build%20&%20Test/badge.svg)
11+
[![Build & Test](https://github.com/getsentry/sentry-javascript/workflows/CI:%20Build%20&%20Test/badge.svg)](https://github.com/getsentry/sentry-javascript/actions)
1212
[![codecov](https://codecov.io/gh/getsentry/sentry-javascript/branch/develop/graph/badge.svg)](https://codecov.io/gh/getsentry/sentry-javascript)
1313
[![npm version](https://img.shields.io/npm/v/@sentry/core.svg)](https://www.npmjs.com/package/@sentry/core)
1414
[![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr)

dev-packages/browser-integration-tests/suites/public-api/logger/integration/subject.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,12 @@ console.log('Mixed:', 'prefix', { obj: true }, [4, 5, 6], 'suffix');
1313

1414
console.log('');
1515

16+
// Test console substitution patterns (should NOT generate template attributes)
17+
console.log('String substitution %s %d', 'test', 42);
18+
console.log('Object substitution %o', { key: 'value' });
19+
20+
// Test multiple arguments without substitutions (should generate template attributes)
21+
console.log('first', 0, 1, 2);
22+
console.log('hello', true, null, undefined);
23+
1624
Sentry.flush();

dev-packages/browser-integration-tests/suites/public-api/logger/integration/test.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
1818
expect(envelopeItems[0]).toEqual([
1919
{
2020
type: 'log',
21-
item_count: 11,
21+
item_count: 15,
2222
content_type: 'application/vnd.sentry.items.log+json',
2323
},
2424
{
@@ -33,6 +33,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
3333
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
3434
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
3535
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
36+
'sentry.message.template': { value: 'console.trace {} {}', type: 'string' },
37+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
38+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
3639
},
3740
},
3841
{
@@ -45,6 +48,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
4548
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
4649
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
4750
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
51+
'sentry.message.template': { value: 'console.debug {} {}', type: 'string' },
52+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
53+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
4854
},
4955
},
5056
{
@@ -57,6 +63,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
5763
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
5864
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
5965
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
66+
'sentry.message.template': { value: 'console.log {} {}', type: 'string' },
67+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
68+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
6069
},
6170
},
6271
{
@@ -69,6 +78,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
6978
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
7079
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
7180
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
81+
'sentry.message.template': { value: 'console.info {} {}', type: 'string' },
82+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
83+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
7284
},
7385
},
7486
{
@@ -81,6 +93,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
8193
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
8294
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
8395
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
96+
'sentry.message.template': { value: 'console.warn {} {}', type: 'string' },
97+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
98+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
8499
},
85100
},
86101
{
@@ -93,6 +108,9 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
93108
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
94109
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
95110
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
111+
'sentry.message.template': { value: 'console.error {} {}', type: 'string' },
112+
'sentry.message.parameter.0': { value: 123, type: 'integer' },
113+
'sentry.message.parameter.1': { value: false, type: 'boolean' },
96114
},
97115
},
98116
{
@@ -117,6 +135,8 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
117135
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
118136
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
119137
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
138+
'sentry.message.template': { value: 'Object: {}', type: 'string' },
139+
'sentry.message.parameter.0': { value: '{"key":"value","nested":{"prop":123}}', type: 'string' },
120140
},
121141
},
122142
{
@@ -129,6 +149,8 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
129149
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
130150
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
131151
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
152+
'sentry.message.template': { value: 'Array: {}', type: 'string' },
153+
'sentry.message.parameter.0': { value: '[1,2,3,"string"]', type: 'string' },
132154
},
133155
},
134156
{
@@ -141,6 +163,11 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
141163
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
142164
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
143165
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
166+
'sentry.message.template': { value: 'Mixed: {} {} {} {}', type: 'string' },
167+
'sentry.message.parameter.0': { value: 'prefix', type: 'string' },
168+
'sentry.message.parameter.1': { value: '{"obj":true}', type: 'string' },
169+
'sentry.message.parameter.2': { value: '[4,5,6]', type: 'string' },
170+
'sentry.message.parameter.3': { value: 'suffix', type: 'string' },
144171
},
145172
},
146173
{
@@ -155,6 +182,62 @@ sentryTest('should capture console object calls', async ({ getLocalTestUrl, page
155182
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
156183
},
157184
},
185+
{
186+
timestamp: expect.any(Number),
187+
level: 'info',
188+
severity_number: 10,
189+
trace_id: expect.any(String),
190+
body: 'String substitution %s %d test 42',
191+
attributes: {
192+
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
193+
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
194+
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
195+
},
196+
},
197+
{
198+
timestamp: expect.any(Number),
199+
level: 'info',
200+
severity_number: 10,
201+
trace_id: expect.any(String),
202+
body: 'Object substitution %o {"key":"value"}',
203+
attributes: {
204+
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
205+
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
206+
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
207+
},
208+
},
209+
{
210+
timestamp: expect.any(Number),
211+
level: 'info',
212+
severity_number: 10,
213+
trace_id: expect.any(String),
214+
body: 'first 0 1 2',
215+
attributes: {
216+
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
217+
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
218+
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
219+
'sentry.message.template': { value: 'first {} {} {}', type: 'string' },
220+
'sentry.message.parameter.0': { value: 0, type: 'integer' },
221+
'sentry.message.parameter.1': { value: 1, type: 'integer' },
222+
'sentry.message.parameter.2': { value: 2, type: 'integer' },
223+
},
224+
},
225+
{
226+
timestamp: expect.any(Number),
227+
level: 'info',
228+
severity_number: 10,
229+
trace_id: expect.any(String),
230+
body: 'hello true null undefined',
231+
attributes: {
232+
'sentry.origin': { value: 'auto.console.logging', type: 'string' },
233+
'sentry.sdk.name': { value: 'sentry.javascript.browser', type: 'string' },
234+
'sentry.sdk.version': { value: expect.any(String), type: 'string' },
235+
'sentry.message.template': { value: 'hello {} {} {}', type: 'string' },
236+
'sentry.message.parameter.0': { value: true, type: 'boolean' },
237+
'sentry.message.parameter.1': { value: 'null', type: 'string' },
238+
'sentry.message.parameter.2': { value: '', type: 'string' },
239+
},
240+
},
158241
],
159242
},
160243
]);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
setTimeout(() => {
14+
Sentry.reportPageLoaded();
15+
}, 2500);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
} from '@sentry/browser';
8+
import { sentryTest } from '../../../../../utils/fixtures';
9+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';
10+
11+
sentryTest(
12+
'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true',
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
19+
20+
const url = await getLocalTestUrl({ testDir: __dirname });
21+
22+
await page.goto(url);
23+
24+
const eventData = envelopeRequestParser(await pageloadEventPromise);
25+
26+
const traceContextData = eventData.contexts?.trace?.data;
27+
const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!;
28+
29+
expect(traceContextData).toMatchObject({
30+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
31+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
32+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
33+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
34+
['sentry.idle_span_finish_reason']: 'reportPageLoaded',
35+
});
36+
37+
// We wait for 2.5 seconds before calling Sentry.reportPageLoaded()
38+
// the margins are to account for timing weirdness in CI to avoid flakes
39+
expect(spanDurationSeconds).toBeGreaterThan(2);
40+
expect(spanDurationSeconds).toBeLessThan(3);
41+
},
42+
);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
// not calling Sentry.reportPageLoaded() on purpose!
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
} from '@sentry/browser';
8+
import { sentryTest } from '../../../../../utils/fixtures';
9+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';
10+
11+
sentryTest(
12+
'final timeout cancels the pageload span even if `enableReportPageLoaded` is true',
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
19+
20+
const url = await getLocalTestUrl({ testDir: __dirname });
21+
22+
await page.goto(url);
23+
24+
const eventData = envelopeRequestParser(await pageloadEventPromise);
25+
26+
const traceContextData = eventData.contexts?.trace?.data;
27+
const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!;
28+
29+
expect(traceContextData).toMatchObject({
30+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
31+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
32+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
33+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
34+
['sentry.idle_span_finish_reason']: 'finalTimeout',
35+
});
36+
37+
// We wait for 3 seconds before calling Sentry.reportPageLoaded()
38+
// the margins are to account for timing weirdness in CI to avoid flakes
39+
expect(spanDurationSeconds).toBeGreaterThan(2.5);
40+
expect(spanDurationSeconds).toBeLessThan(3.5);
41+
},
42+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
setTimeout(() => {
14+
Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(), { name: 'custom_navigation' });
15+
}, 1000);
16+
17+
setTimeout(() => {
18+
Sentry.reportPageLoaded();
19+
}, 2500);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
} from '@sentry/browser';
8+
import { sentryTest } from '../../../../../utils/fixtures';
9+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';
10+
11+
sentryTest(
12+
'starting a navigation span cancels the pageload span even if `enableReportPageLoaded` is true',
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
19+
20+
const url = await getLocalTestUrl({ testDir: __dirname });
21+
22+
await page.goto(url);
23+
24+
const eventData = envelopeRequestParser(await pageloadEventPromise);
25+
26+
const traceContextData = eventData.contexts?.trace?.data;
27+
const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!;
28+
29+
expect(traceContextData).toMatchObject({
30+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
31+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
32+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
33+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
34+
['sentry.idle_span_finish_reason']: 'cancelled',
35+
});
36+
37+
// ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes
38+
expect(spanDurationSeconds).toBeLessThan(1.5);
39+
},
40+
);

0 commit comments

Comments
 (0)