Skip to content

Commit eab72fe

Browse files
authored
Merge pull request #8063 from getsentry/prepare-release/7.51.1
meta(changelog): Update changelog for 7.51.1
2 parents fa81a1b + 7303fd1 commit eab72fe

Some content is hidden

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

43 files changed

+854
-317
lines changed

CHANGELOG.md

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

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

7+
## 7.51.1
8+
9+
- feat(replay): Add event to capture options on checkouts (#8011)
10+
- feat(replay): Improve click target detection (#8026)
11+
- fix(node): Make sure we use same ID for checkIns (#8050)
12+
- fix(replay: Keep session active on key press (#8037)
13+
- fix(replay): Move error sampling to before send (#8057)
14+
- fix(sveltekit): Wrap `load` when typed explicitly (#8049)
15+
16+
**Replay `rrweb` changes:**
17+
18+
`@sentry-internal/rrweb` was updated from 1.106.0 to 1.108.0:
19+
20+
- fix: Fix some input masking (esp for radio buttons) ([#85](https://github.com/getsentry/rrweb/pull/85))
21+
- fix: Unescaped `:` in CSS rule from Safari ([#86](https://github.com/getsentry/rrweb/pull/86))
22+
- feat: Define custom elements (web components) ([#87](https://github.com/getsentry/rrweb/pull/87))
23+
24+
Work in this release contributed by @sreetamdas. Thank you for your contribution!
25+
726
## 7.51.0
827

928
### Important Changes
@@ -26,30 +45,40 @@ Note that `@sentry/angular` _does not_ support Angular 16.
2645

2746
- **feat(node): Add ability to send cron monitor check ins (#8039)**
2847

48+
**Note: This release contains a bug with generating cron monitors. We recommend you upgrade the JS SDK to 7.51.1 or above to use cron monitoring functionality**
49+
2950
This release adds [Sentry cron monitoring](https://docs.sentry.io/product/crons/) support to the Node SDK.
3051

31-
To monitor your cron jobs, send check-ins everytime you execute your cron jobs to Sentry. You can do this with the `captureCheckIn` method exported from the SDK. First you must send an `in_progress`, checkin, then you can send one with status `ok` or `error` based on what happened with your cron job.
52+
Check-in monitoring allows you to track a job's progress by completing two check-ins: one at the start of your job and another at the end of your job. This two-step process allows Sentry to notify you if your job didn't start when expected (missed) or if it exceeded its maximum runtime (failed).
3253

3354
```ts
3455
const Sentry = require('@sentry/node');
3556

36-
// ...
37-
38-
Sentry.captureCheckIn({
39-
// make sure this is the same slug as what you set up your
40-
// Sentry cron monitor with.
41-
monitorSlug: 'dailyEmail',
57+
// 🟡 Notify Sentry your job is running:
58+
const checkInId = Sentry.captureCheckIn({
59+
monitorSlug: '<monitor-slug>',
4260
status: 'in_progress',
4361
});
4462

45-
const startTime = timeInSeconds();
46-
47-
runTask();
63+
// Execute your scheduled task here...
4864

65+
// 🟢 Notify Sentry your job has completed successfully:
4966
Sentry.captureCheckIn({
50-
monitorSlug: 'dailyEmail',
67+
// make sure you pass in the checkInId generated by the first call to captureCheckIn
68+
checkInId,
69+
monitorSlug: '<monitor-slug>',
5170
status: 'ok',
52-
duration: timeInSeconds() - startTime,
71+
});
72+
```
73+
74+
If your job execution fails, you can notify Sentry about the failure:
75+
76+
```javascript
77+
// 🔴 Notify Sentry your job has failed:
78+
Sentry.captureCheckIn({
79+
checkInId,
80+
monitorSlug: '<monitor-slug>',
81+
status: 'error',
5382
});
5483
```
5584

packages/browser-integration-tests/suites/replay/bufferMode/test.ts

Lines changed: 114 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ sentryTest(
115115

116116
expect(event0).toEqual(
117117
getExpectedReplayEvent({
118-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
119118
error_ids: [errorEventId!],
120119
replay_type: 'buffer',
121120
}),
@@ -150,7 +149,6 @@ sentryTest(
150149

151150
expect(event1).toEqual(
152151
getExpectedReplayEvent({
153-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
154152
replay_type: 'buffer', // although we're in session mode, we still send 'buffer' as replay_type
155153
segment_id: 1,
156154
urls: [],
@@ -162,7 +160,6 @@ sentryTest(
162160

163161
expect(event2).toEqual(
164162
getExpectedReplayEvent({
165-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
166163
replay_type: 'buffer', // although we're in session mode, we still send 'buffer' as replay_type
167164
segment_id: 2,
168165
urls: [],
@@ -266,7 +263,6 @@ sentryTest(
266263

267264
expect(event0).toEqual(
268265
getExpectedReplayEvent({
269-
contexts: { replay: { error_sample_rate: 0, session_sample_rate: 0 } },
270266
error_ids: [errorEventId!],
271267
replay_type: 'buffer',
272268
}),
@@ -303,118 +299,126 @@ sentryTest(
303299

304300
// Doing this in buffer mode to test changing error sample rate after first
305301
// error happens.
306-
sentryTest('[buffer-mode] can sample on each error event', async ({ getLocalTestPath, page, browserName }) => {
307-
// This was sometimes flaky on firefox/webkit, so skipping for now
308-
if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) {
309-
sentryTest.skip();
310-
}
311-
312-
let callsToSentry = 0;
313-
const errorEventIds: string[] = [];
314-
const reqPromise0 = waitForReplayRequest(page, 0);
315-
const reqErrorPromise = waitForErrorRequest(page);
316-
317-
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
318-
const event = envelopeRequestParser(route.request());
319-
// error events have no type field
320-
if (event && !event.type && event.event_id) {
321-
errorEventIds.push(event.event_id);
322-
}
323-
// We only want to count errors & replays here
324-
if (event && (!event.type || isReplayEvent(event))) {
325-
callsToSentry++;
302+
sentryTest(
303+
'[buffer-mode] can sample on each error event',
304+
async ({ getLocalTestPath, page, browserName, enableConsole }) => {
305+
// This was sometimes flaky on firefox/webkit, so skipping for now
306+
if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) {
307+
sentryTest.skip();
326308
}
327309

328-
return route.fulfill({
329-
status: 200,
330-
contentType: 'application/json',
331-
body: JSON.stringify({ id: 'test-id' }),
310+
enableConsole();
311+
312+
let callsToSentry = 0;
313+
const errorEventIds: string[] = [];
314+
const reqPromise0 = waitForReplayRequest(page, 0);
315+
const reqErrorPromise0 = waitForErrorRequest(page);
316+
317+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
318+
const event = envelopeRequestParser(route.request());
319+
// error events have no type field
320+
if (event && !event.type && event.event_id) {
321+
errorEventIds.push(event.event_id);
322+
}
323+
// We only want to count errors & replays here
324+
if (event && (!event.type || isReplayEvent(event))) {
325+
callsToSentry++;
326+
}
327+
328+
return route.fulfill({
329+
status: 200,
330+
contentType: 'application/json',
331+
body: JSON.stringify({ id: 'test-id' }),
332+
});
333+
});
334+
335+
const url = await getLocalTestPath({ testDir: __dirname });
336+
337+
await page.goto(url);
338+
// Start buffering and assert that it is enabled
339+
expect(
340+
await page.evaluate(() => {
341+
const replayIntegration = (window as unknown as Window & { Replay: InstanceType<typeof Replay> }).Replay;
342+
const replay = replayIntegration['_replay'];
343+
replayIntegration.startBuffering();
344+
return replay.isEnabled();
345+
}),
346+
).toBe(true);
347+
348+
await page.click('#go-background');
349+
await page.click('#error');
350+
await new Promise(resolve => setTimeout(resolve, 1000));
351+
352+
// 1 unsampled error, no replay
353+
const reqError0 = await reqErrorPromise0;
354+
const errorEvent0 = envelopeRequestParser(reqError0);
355+
expect(callsToSentry).toEqual(1);
356+
expect(errorEvent0.tags?.replayId).toBeUndefined();
357+
358+
await page.evaluate(async () => {
359+
const replayIntegration = (window as unknown as Window & { Replay: Replay }).Replay;
360+
replayIntegration['_replay'].getOptions().errorSampleRate = 1.0;
332361
});
333-
});
334-
335-
const url = await getLocalTestPath({ testDir: __dirname });
336-
337-
await page.goto(url);
338-
// Start buffering and assert that it is enabled
339-
expect(
340-
await page.evaluate(() => {
341-
const replayIntegration = (window as unknown as Window & { Replay: InstanceType<typeof Replay> }).Replay;
342-
const replay = replayIntegration['_replay'];
343-
replayIntegration.startBuffering();
344-
return replay.isEnabled();
345-
}),
346-
).toBe(true);
347-
348-
await page.click('#go-background');
349-
await page.click('#error');
350-
await new Promise(resolve => setTimeout(resolve, 1000));
351-
352-
// 1 error, no replay
353-
await reqErrorPromise;
354-
expect(callsToSentry).toEqual(1);
355-
356-
await page.evaluate(async () => {
357-
const replayIntegration = (window as unknown as Window & { Replay: Replay }).Replay;
358-
replayIntegration['_replay'].getOptions().errorSampleRate = 1.0;
359-
});
360-
361-
// Error sample rate is now at 1.0, this error should create a replay
362-
await page.click('#error2');
363-
364-
const req0 = await reqPromise0;
365-
366-
// 2 errors, 1 flush
367-
await reqErrorPromise;
368-
expect(callsToSentry).toEqual(3);
369-
370-
const event0 = getReplayEvent(req0);
371-
const content0 = getReplayRecordingContent(req0);
372-
373-
expect(event0).toEqual(
374-
getExpectedReplayEvent({
375-
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
376-
error_ids: errorEventIds,
377-
replay_type: 'buffer',
378-
}),
379-
);
380-
381-
// The first event should have both, full and incremental snapshots,
382-
// as we recorded and kept all events in the buffer
383-
expect(content0.fullSnapshots).toHaveLength(1);
384-
// We want to make sure that the event that triggered the error was
385-
// recorded, as well as the first error that did not get sampled.
386-
expect(content0.breadcrumbs).toEqual(
387-
expect.arrayContaining([
388-
{
389-
...expectedClickBreadcrumb,
390-
message: 'body > button#error',
391-
data: {
392-
nodeId: expect.any(Number),
393-
node: {
394-
attributes: {
395-
id: 'error',
362+
363+
// Error sample rate is now at 1.0, this error should create a replay
364+
const reqErrorPromise1 = waitForErrorRequest(page);
365+
await page.click('#error2');
366+
// 1 unsampled error, 1 sampled error -> 1 flush
367+
const req0 = await reqPromise0;
368+
const reqError1 = await reqErrorPromise1;
369+
const errorEvent1 = envelopeRequestParser(reqError1);
370+
expect(callsToSentry).toEqual(3);
371+
expect(errorEvent0.event_id).not.toEqual(errorEvent1.event_id);
372+
expect(errorEvent1.tags?.replayId).toBeDefined();
373+
374+
const event0 = getReplayEvent(req0);
375+
const content0 = getReplayRecordingContent(req0);
376+
377+
expect(event0).toEqual(
378+
getExpectedReplayEvent({
379+
error_ids: errorEventIds,
380+
replay_type: 'buffer',
381+
}),
382+
);
383+
384+
// The first event should have both, full and incremental snapshots,
385+
// as we recorded and kept all events in the buffer
386+
expect(content0.fullSnapshots).toHaveLength(1);
387+
// We want to make sure that the event that triggered the error was
388+
// recorded, as well as the first error that did not get sampled.
389+
expect(content0.breadcrumbs).toEqual(
390+
expect.arrayContaining([
391+
{
392+
...expectedClickBreadcrumb,
393+
message: 'body > button#error',
394+
data: {
395+
nodeId: expect.any(Number),
396+
node: {
397+
attributes: {
398+
id: 'error',
399+
},
400+
id: expect.any(Number),
401+
tagName: 'button',
402+
textContent: '***** *****',
396403
},
397-
id: expect.any(Number),
398-
tagName: 'button',
399-
textContent: '***** *****',
400404
},
401405
},
402-
},
403-
{
404-
...expectedClickBreadcrumb,
405-
message: 'body > button#error2',
406-
data: {
407-
nodeId: expect.any(Number),
408-
node: {
409-
attributes: {
410-
id: 'error2',
406+
{
407+
...expectedClickBreadcrumb,
408+
message: 'body > button#error2',
409+
data: {
410+
nodeId: expect.any(Number),
411+
node: {
412+
attributes: {
413+
id: 'error2',
414+
},
415+
id: expect.any(Number),
416+
tagName: 'button',
417+
textContent: '******* *****',
411418
},
412-
id: expect.any(Number),
413-
tagName: 'button',
414-
textContent: '******* *****',
415419
},
416420
},
417-
},
418-
]),
419-
);
420-
});
421+
]),
422+
);
423+
},
424+
);

packages/browser-integration-tests/suites/replay/captureReplay/test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
6464
},
6565
},
6666
platform: 'javascript',
67-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
6867
});
6968

7069
expect(replayEvent1).toBeDefined();
@@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/browser export)', async ({ getLocalT
103102
},
104103
},
105104
platform: 'javascript',
106-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
107105
});
108106
});

packages/browser-integration-tests/suites/replay/captureReplayFromReplayPackage/test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
6464
},
6565
},
6666
platform: 'javascript',
67-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
6867
});
6968

7069
expect(replayEvent1).toBeDefined();
@@ -103,6 +102,5 @@ sentryTest('should capture replays (@sentry/replay export)', async ({ getLocalTe
103102
},
104103
},
105104
platform: 'javascript',
106-
contexts: { replay: { session_sample_rate: 1, error_sample_rate: 0 } },
107105
});
108106
});

packages/browser-integration-tests/suites/replay/customEvents/template.html

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,9 @@
55
</head>
66
<body>
77
<div role="button" id="error" class="btn btn-error" aria-label="An Error">An Error</div>
8-
<button>
9-
<img id="img"
10-
alt="Alt Text"
11-
/>
12-
</button>
13-
<button class="sentry-unmask" aria-label="Unmasked label">
14-
Unmasked
8+
<button title="Button title">
9+
<img id="img" alt="Alt Text" />
1510
</button>
11+
<button class="sentry-unmask" aria-label="Unmasked label">Unmasked</button>
1612
</body>
1713
</html>

0 commit comments

Comments
 (0)