Skip to content

Commit

Permalink
ref(replay): Log warning if sample rates are all undefined (getsentry…
Browse files Browse the repository at this point in the history
…#6959)

Since we decided to set both replay sample rates to 0 by default, we should log a warning that replay will be disabled if no sample rates were passed to Sentry.init. Since we're still supporting the deprecated sample rates in the integration options, this warning will only be emitted if these rates aren't set either.
  • Loading branch information
Lms24 authored Jan 30, 2023
1 parent e4e7e2e commit 15ec85b
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 21 deletions.
73 changes: 53 additions & 20 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getCurrentHub } from '@sentry/core';
import type { BrowserClientReplayOptions, Integration } from '@sentry/types';
import { dropUndefinedKeys } from '@sentry/utils';

import { DEFAULT_FLUSH_MAX_DELAY, DEFAULT_FLUSH_MIN_DELAY, MASK_ALL_TEXT_SELECTOR } from './constants';
import { ReplayContainer } from './replay';
Expand All @@ -11,6 +12,9 @@ const MEDIA_SELECTORS = 'img,image,svg,path,rect,area,video,object,picture,embed

let _initialized = false;

type InitialReplayPluginOptions = Omit<ReplayPluginOptions, 'sessionSampleRate' | 'errorSampleRate'> &
Partial<Pick<ReplayPluginOptions, 'sessionSampleRate' | 'errorSampleRate'>>;

/**
* The main replay integration class, to be passed to `init({ integrations: [] })`.
*/
Expand All @@ -30,7 +34,14 @@ export class Replay implements Integration {
*/
private readonly _recordingOptions: RecordingOptions;

private readonly _options: ReplayPluginOptions;
/**
* Initial options passed to the replay integration, merged with default values.
* Note: `sessionSampleRate` and `errorSampleRate` are not required here, as they
* can only be finally set when setupOnce() is called.
*
* @private
*/
private readonly _initialOptions: InitialReplayPluginOptions;

private _replay?: ReplayContainer;

Expand Down Expand Up @@ -92,12 +103,12 @@ export class Replay implements Integration {
collectFonts: true,
};

this._options = {
this._initialOptions = {
flushMinDelay,
flushMaxDelay,
stickySession,
sessionSampleRate: 0,
errorSampleRate: 0,
sessionSampleRate,
errorSampleRate,
useCompression,
maskAllText: typeof maskAllText === 'boolean' ? maskAllText : !maskTextSelector,
blockAllMedia,
Expand All @@ -113,7 +124,7 @@ Instead, configure \`replaysSessionSampleRate\` directly in the SDK init options
Sentry.init({ replaysSessionSampleRate: ${sessionSampleRate} })`,
);

this._options.sessionSampleRate = sessionSampleRate;
this._initialOptions.sessionSampleRate = sessionSampleRate;
}

if (typeof errorSampleRate === 'number') {
Expand All @@ -125,17 +136,17 @@ Instead, configure \`replaysOnErrorSampleRate\` directly in the SDK init options
Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
);

this._options.errorSampleRate = errorSampleRate;
this._initialOptions.errorSampleRate = errorSampleRate;
}

if (this._options.maskAllText) {
if (this._initialOptions.maskAllText) {
// `maskAllText` is a more user friendly option to configure
// `maskTextSelector`. This means that all nodes will have their text
// content masked.
this._recordingOptions.maskTextSelector = MASK_ALL_TEXT_SELECTOR;
}

if (this._options.blockAllMedia) {
if (this._initialOptions.blockAllMedia) {
// `blockAllMedia` is a more user friendly option to configure blocking
// embedded media elements
this._recordingOptions.blockSelector = !this._recordingOptions.blockSelector
Expand Down Expand Up @@ -221,25 +232,47 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
/** Setup the integration. */
private _setup(): void {
// Client is not available in constructor, so we need to wait until setupOnce
this._loadReplayOptionsFromClient();
const finalOptions = loadReplayOptionsFromClient(this._initialOptions);

this._replay = new ReplayContainer({
options: this._options,
options: finalOptions,
recordingOptions: this._recordingOptions,
});
}
}

/** Parse Replay-related options from SDK options */
private _loadReplayOptionsFromClient(): void {
const client = getCurrentHub().getClient();
const opt = client && (client.getOptions() as BrowserClientReplayOptions | undefined);
/** Parse Replay-related options from SDK options */
function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions): ReplayPluginOptions {
const client = getCurrentHub().getClient();
const opt = client && (client.getOptions() as BrowserClientReplayOptions);

if (opt && typeof opt.replaysSessionSampleRate === 'number') {
this._options.sessionSampleRate = opt.replaysSessionSampleRate;
}
const finalOptions = { sessionSampleRate: 0, errorSampleRate: 0, ...dropUndefinedKeys(initialOptions) };

if (opt && typeof opt.replaysOnErrorSampleRate === 'number') {
this._options.errorSampleRate = opt.replaysOnErrorSampleRate;
}
if (!opt) {
// eslint-disable-next-line no-console
console.warn('SDK client is not available.');
return finalOptions;
}

if (
initialOptions.sessionSampleRate == null && // TODO remove once deprecated rates are removed
initialOptions.errorSampleRate == null && // TODO remove once deprecated rates are removed
opt.replaysSessionSampleRate == null &&
opt.replaysOnErrorSampleRate == null
) {
// eslint-disable-next-line no-console
console.warn(
'Replay is disabled because neither `replaysSessionSampleRate` nor `replaysOnErrorSampleRate` are set.',
);
}

if (typeof opt.replaysSessionSampleRate === 'number') {
finalOptions.sessionSampleRate = opt.replaysSessionSampleRate;
}

if (typeof opt.replaysOnErrorSampleRate === 'number') {
finalOptions.errorSampleRate = opt.replaysOnErrorSampleRate;
}

return finalOptions;
}
25 changes: 24 additions & 1 deletion packages/replay/test/integration/integrationSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Integration | integrationSettings', () => {
expect(mockConsole).toBeCalledTimes(1);
});

it('works with defining 0 in integration', async () => {
it('works with defining 0 in integration but logs warnings', async () => {
const { replay } = await mockSdk({
replayOptions: { sessionSampleRate: 0 },
sentryOptions: { replaysSessionSampleRate: undefined },
Expand Down Expand Up @@ -164,6 +164,29 @@ describe('Integration | integrationSettings', () => {
});
});

describe('all sample rates', () => {
let mockConsole: jest.SpyInstance<void>;

beforeEach(() => {
mockConsole = jest.spyOn(console, 'warn').mockImplementation(jest.fn());
});

afterEach(() => {
mockConsole.mockRestore();
});

it('logs warning if no sample rates are set', async () => {
const { replay } = await mockSdk({
sentryOptions: { replaysOnErrorSampleRate: undefined, replaysSessionSampleRate: undefined },
replayOptions: {},
});

expect(replay.getOptions().sessionSampleRate).toBe(0);
expect(replay.getOptions().errorSampleRate).toBe(0);
expect(mockConsole).toBeCalledTimes(1);
});
});

describe('maskAllText', () => {
it('works with default value', async () => {
const { replay } = await mockSdk({ replayOptions: {} });
Expand Down

0 comments on commit 15ec85b

Please sign in to comment.