Skip to content

[Gitflow] Merge develop into master #7318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e4941fc
Merge pull request #7285 from getsentry/master
github-actions[bot] Feb 27, 2023
3324324
fix(ember): Disable performance in FastBoot (#7282)
mydea Feb 27, 2023
e471837
ci: Move replay metrics into dedicated package (#7115)
mydea Feb 28, 2023
67b0684
test(replay): Fix flaky xhr/fetch request tests (#7270)
Lms24 Feb 28, 2023
4ae08fe
test(replay): Fix flaky flush test (#7268)
Lms24 Feb 28, 2023
7029da4
fix(serverless): Capture custom tags in error events of GCP functions…
Lms24 Feb 28, 2023
64a1b09
fix(serverless): Capture custom tags in GCP Background and CloudEvent…
Lms24 Feb 28, 2023
dbd7a81
build(deps): Bump minimist from 0.2.1 to 0.2.4 (#7296)
dependabot[bot] Feb 28, 2023
70abc37
ci: Add new metrics overhead app (#7300)
mydea Mar 1, 2023
61405d4
chore(angular-ivy): Add release registry config for `@sentry/angular-…
Lms24 Mar 1, 2023
3ba8e0b
test(browser): Temporarily skip offline transport tests (#7305)
Lms24 Mar 1, 2023
a6b65c4
ci: Better error handling when caches can't be restored (#7306)
mydea Mar 1, 2023
73a1bbb
ci: Avoid `continue-on-error` for nextjs tests (#7308)
mydea Mar 1, 2023
ca1352d
test(core): Skip flaky offline transport test (#7312)
Lms24 Mar 1, 2023
8a5e0c3
test: Fix flaky errorsInSession test (#7309)
mydea Mar 1, 2023
b887fb7
ci: Fix overhead measurements runs on default branch (#7313)
mydea Mar 1, 2023
72cb01f
feat(nextjs): Automatically resolve source of errors in dev mode (#7294)
Mar 1, 2023
eedd811
feat(vue): Log errors to the console by default (#7310)
Lms24 Mar 1, 2023
f5669af
test: Fix flaky replay test (#7307)
mydea Mar 1, 2023
3977189
ref(replay): Use `SESSION_IDLE_DURATION` instead of `VISIBILITY_CHANG…
mydea Mar 1, 2023
648ec0a
Merge branch 'develop' into fn/fix-master
mydea Mar 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions packages/integration-tests/suites/replay/errors/errorMode/test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect } from '@playwright/test';

import { sentryTest } from '../../../../utils/fixtures';
import { envelopeRequestParser } from '../../../../utils/helpers';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers';
import {
expectedClickBreadcrumb,
expectedConsoleBreadcrumb,
Expand All @@ -10,6 +10,7 @@ import {
import {
getReplayEvent,
getReplayRecordingContent,
isReplayEvent,
shouldSkipReplayTest,
waitForReplayRequest,
} from '../../../../utils/replayHelpers';
Expand All @@ -26,14 +27,18 @@ sentryTest(
const reqPromise0 = waitForReplayRequest(page, 0);
const reqPromise1 = waitForReplayRequest(page, 1);
const reqPromise2 = waitForReplayRequest(page, 2);
const reqErrorPromise = waitForErrorRequest(page);

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
const event = envelopeRequestParser(route.request());
// error events have no type field
if (event && !event.type && event.event_id) {
errorEventId = event.event_id;
}
callsToSentry++;
// We only want to count errors & replays here
if (event && (!event.type || isReplayEvent(event))) {
callsToSentry++;
}

return route.fulfill({
status: 200,
Expand All @@ -46,13 +51,16 @@ sentryTest(

await page.goto(url);
await page.click('#go-background');
await new Promise(resolve => setTimeout(resolve, 1000));

expect(callsToSentry).toEqual(0);

await page.click('#error');
const req0 = await reqPromise0;

await page.click('#go-background');
const req1 = await reqPromise1;
await reqErrorPromise;

expect(callsToSentry).toEqual(3); // 1 error, 2 replay events

Expand All @@ -69,11 +77,12 @@ sentryTest(
const event2 = getReplayEvent(req2);
const content2 = getReplayRecordingContent(req2);

expect(callsToSentry).toBe(4); // 1 error, 3 replay events

expect(event0).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
// @ts-ignore this is fine
error_ids: [errorEventId],
error_ids: [errorEventId!],
replay_type: 'error',
}),
);
Expand All @@ -97,7 +106,6 @@ sentryTest(
expect(event1).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
// @ts-ignore this is fine
replay_type: 'error', // although we're in session mode, we still send 'error' as replay_type
replay_start_timestamp: undefined,
segment_id: 1,
Expand All @@ -108,14 +116,12 @@ sentryTest(
// Also the second snapshot should have a full snapshot, as we switched from error to session
// mode which triggers another checkout
expect(content1.fullSnapshots).toHaveLength(1);
expect(content1.incrementalSnapshots).toHaveLength(0);

// The next event should just be a normal replay event as we're now in session mode and
// we continue recording everything
expect(event2).toEqual(
getExpectedReplayEvent({
contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } },
// @ts-ignore this is fine
replay_type: 'error',
replay_start_timestamp: undefined,
segment_id: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ sentryTest(
const url = await getLocalTestPath({ testDir: __dirname });

await page.goto(url);
await page.click('#go-background');
const req0 = await reqPromise0;

await page.click('#error');
Expand All @@ -57,7 +56,6 @@ sentryTest(
getExpectedReplayEvent({
replay_start_timestamp: undefined,
segment_id: 1,
// @ts-ignore this is fine
error_ids: [errorEventId],
urls: [],
}),
Expand Down
4 changes: 2 additions & 2 deletions packages/integration-tests/suites/replay/errors/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;
window.Replay = new Sentry.Replay({
flushMinDelay: 500,
flushMaxDelay: 500,
flushMinDelay: 1000,
flushMaxDelay: 1000,
});

Sentry.init({
Expand Down
17 changes: 17 additions & 0 deletions packages/integration-tests/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ async function getSentryEvents(page: Page, url?: string): Promise<Array<Event>>
return eventsHandle.jsonValue();
}

export function waitForErrorRequest(page: Page): Promise<Request> {
return page.waitForRequest(req => {
const postData = req.postData();
if (!postData) {
return false;
}

try {
const event = envelopeRequestParser(req);

return !event.type;
} catch {
return false;
}
});
}

/**
* Waits until a number of requests matching urlRgx at the given URL arrive.
* If the timout option is configured, this function will abort waiting, even if it hasn't reveived the configured
Expand Down
2 changes: 1 addition & 1 deletion packages/integration-tests/utils/replayHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function waitForReplayRequest(page: Page, segmentId?: number): Promise<Re
});
}

function isReplayEvent(event: Event): event is ReplayEvent {
export function isReplayEvent(event: Event): event is ReplayEvent {
return event.type === 'replay_event';
}

Expand Down
3 changes: 0 additions & 3 deletions packages/replay/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay';
// The idle limit for a session
export const SESSION_IDLE_DURATION = 300_000; // 5 minutes in ms

// Grace period to keep a session when a user changes tabs or hides window
export const VISIBILITY_CHANGE_TIMEOUT = SESSION_IDLE_DURATION;

// The maximum length of a session
export const MAX_SESSION_LIFE = 3_600_000; // 60 minutes

Expand Down
22 changes: 8 additions & 14 deletions packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ import { captureException, getCurrentHub } from '@sentry/core';
import type { Breadcrumb, ReplayRecordingMode } from '@sentry/types';
import { logger } from '@sentry/utils';

import {
ERROR_CHECKOUT_TIME,
MAX_SESSION_LIFE,
SESSION_IDLE_DURATION,
VISIBILITY_CHANGE_TIMEOUT,
WINDOW,
} from './constants';
import { ERROR_CHECKOUT_TIME, MAX_SESSION_LIFE, SESSION_IDLE_DURATION, WINDOW } from './constants';
import { setupPerformanceObserver } from './coreHandlers/performanceObserver';
import { createEventBuffer } from './eventBuffer';
import { getSession } from './session/getSession';
Expand Down Expand Up @@ -367,7 +361,7 @@ export class ReplayContainer implements ReplayContainerInterface {
* Returns true if session is not expired, false otherwise.
* @hidden
*/
public checkAndHandleExpiredSession(expiry?: number): boolean | void {
public checkAndHandleExpiredSession(): boolean | void {
const oldSessionId = this.getSessionId();

// Prevent starting a new session if the last user activity is older than
Expand All @@ -382,7 +376,7 @@ export class ReplayContainer implements ReplayContainerInterface {

// --- There is recent user activity --- //
// This will create a new session if expired, based on expiry length
if (!this._loadAndCheckSession(expiry)) {
if (!this._loadAndCheckSession()) {
return;
}

Expand Down Expand Up @@ -412,9 +406,9 @@ export class ReplayContainer implements ReplayContainerInterface {
* Loads (or refreshes) the current session.
* Returns false if session is not recorded.
*/
private _loadAndCheckSession(expiry = SESSION_IDLE_DURATION): boolean {
private _loadAndCheckSession(): boolean {
const { type, session } = getSession({
expiry,
expiry: SESSION_IDLE_DURATION,
stickySession: Boolean(this._options.stickySession),
currentSession: this.session,
sessionSampleRate: this._options.sessionSampleRate,
Expand Down Expand Up @@ -626,7 +620,7 @@ export class ReplayContainer implements ReplayContainerInterface {
return;
}

const expired = isSessionExpired(this.session, VISIBILITY_CHANGE_TIMEOUT);
const expired = isSessionExpired(this.session, SESSION_IDLE_DURATION);

if (breadcrumb && !expired) {
this._createCustomBreadcrumb(breadcrumb);
Expand All @@ -646,10 +640,10 @@ export class ReplayContainer implements ReplayContainerInterface {
return;
}

const isSessionActive = this.checkAndHandleExpiredSession(VISIBILITY_CHANGE_TIMEOUT);
const isSessionActive = this.checkAndHandleExpiredSession();

if (!isSessionActive) {
// If the user has come back to the page within VISIBILITY_CHANGE_TIMEOUT
// If the user has come back to the page within SESSION_IDLE_DURATION
// ms, we will re-use the existing session, otherwise create a new
// session
__DEBUG_BUILD__ && logger.log('[Replay] Document has become active, but session has expired');
Expand Down
10 changes: 5 additions & 5 deletions packages/replay/test/integration/errorSampleRate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ERROR_CHECKOUT_TIME,
MAX_SESSION_LIFE,
REPLAY_SESSION_KEY,
VISIBILITY_CHANGE_TIMEOUT,
SESSION_IDLE_DURATION,
WINDOW,
} from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
Expand Down Expand Up @@ -154,15 +154,15 @@ describe('Integration | errorSampleRate', () => {
});
});

it('does not send a replay when triggering a full dom snapshot when document becomes visible after [VISIBILITY_CHANGE_TIMEOUT]ms', async () => {
it('does not send a replay when triggering a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', async () => {
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
return 'visible';
},
});

jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT + 1);
jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1);

document.dispatchEvent(new Event('visibilitychange'));

Expand All @@ -186,8 +186,8 @@ describe('Integration | errorSampleRate', () => {

expect(replay).not.toHaveLastSentReplay();

// User comes back before `VISIBILITY_CHANGE_TIMEOUT` elapses
jest.advanceTimersByTime(VISIBILITY_CHANGE_TIMEOUT - 100);
// User comes back before `SESSION_IDLE_DURATION` elapses
jest.advanceTimersByTime(SESSION_IDLE_DURATION - 100);
Object.defineProperty(document, 'visibilityState', {
configurable: true,
get: function () {
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/test/integration/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Integration | events', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();
mockTransportSend.mockClear();
});

Expand Down Expand Up @@ -93,7 +93,7 @@ describe('Integration | events', () => {

it('has correct timestamps when there are events earlier than initial timestamp', async function () {
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();
mockTransportSend.mockClear();
Object.defineProperty(document, 'visibilityState', {
configurable: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/test/integration/flush.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as SentryUtils from '@sentry/utils';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY, WINDOW } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import type { EventBuffer } from '../../src/types';
import * as AddMemoryEntry from '../../src/util/addMemoryEntry';
Expand Down Expand Up @@ -95,7 +95,7 @@ describe('Integration | flush', () => {
jest.setSystemTime(new Date(BASE_TIMESTAMP));
sessionStorage.clear();
clearSession(replay);
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
mockRecord.takeFullSnapshot.mockClear();
Object.defineProperty(WINDOW, 'location', {
value: prevLocation,
Expand Down
6 changes: 3 additions & 3 deletions packages/replay/test/integration/rateLimiting.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getCurrentHub } from '@sentry/core';
import type { Transport } from '@sentry/types';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import * as SendReplayRequest from '../../src/util/sendReplayRequest';
import { BASE_TIMESTAMP, mockSdk } from '../index';
Expand Down Expand Up @@ -46,7 +46,7 @@ describe('Integration | rate-limiting behaviour', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();

mockSendReplayRequest.mockClear();
});
Expand All @@ -57,7 +57,7 @@ describe('Integration | rate-limiting behaviour', () => {
jest.setSystemTime(new Date(BASE_TIMESTAMP));
clearSession(replay);
jest.clearAllMocks();
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
});

afterAll(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/replay/test/integration/sendReplayEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as SentryCore from '@sentry/core';
import type { Transport } from '@sentry/types';
import * as SentryUtils from '@sentry/utils';

import { DEFAULT_FLUSH_MIN_DELAY, SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { DEFAULT_FLUSH_MIN_DELAY, WINDOW } from '../../src/constants';
import type { ReplayContainer } from '../../src/replay';
import { addEvent } from '../../src/util/addEvent';
import * as SendReplayRequest from '../../src/util/sendReplayRequest';
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('Integration | sendReplayEvent', () => {
// Create a new session and clear mocks because a segment (from initial
// checkout) will have already been uploaded by the time the tests run
clearSession(replay);
replay['_loadAndCheckSession'](0);
replay['_loadAndCheckSession']();

mockSendReplayRequest.mockClear();
});
Expand All @@ -69,7 +69,7 @@ describe('Integration | sendReplayEvent', () => {
await new Promise(process.nextTick);
jest.setSystemTime(new Date(BASE_TIMESTAMP));
clearSession(replay);
replay['_loadAndCheckSession'](SESSION_IDLE_DURATION);
replay['_loadAndCheckSession']();
});

afterAll(() => {
Expand Down
Loading