Skip to content

Commit c2ae9bd

Browse files
authored
feat(browser): Add interactionsSampleRate to browserTracingIntegration options (#12023)
This patch forward-ports the `interactionsSampleRate` option for `browserTracingIntegration` introduced in 7.110.0 to v8. Looks like we missed this when forward-porting the INP implementation as reported in #12006, probably because these two things happened in parallel.
1 parent 012b2c4 commit c2ae9bd

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

packages/browser-utils/src/metrics/inp.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import { getBrowserPerformanceAPI, msToSec } from './utils';
1717
/**
1818
* Start tracking INP webvital events.
1919
*/
20-
export function startTrackingINP(): () => void {
20+
export function startTrackingINP(interactionsSampleRate: number): () => void {
2121
const performance = getBrowserPerformanceAPI();
2222
if (performance && browserPerformanceTimeOrigin) {
23-
const inpCallback = _trackINP();
23+
const inpCallback = _trackINP(interactionsSampleRate);
2424

2525
return (): void => {
2626
inpCallback();
@@ -60,8 +60,16 @@ const INP_ENTRY_MAP: Record<string, 'click' | 'hover' | 'drag' | 'press'> = {
6060
};
6161

6262
/** Starts tracking the Interaction to Next Paint on the current page. */
63-
function _trackINP(): () => void {
63+
function _trackINP(interactionsSampleRate: number): () => void {
6464
return addInpInstrumentationHandler(({ metric }) => {
65+
// As specified in the `interactionsSampleRate` option, the sampling decision shall be based on
66+
// `tracesSampleRate` x `interactionsSampleRate`
67+
// This is the same as sampling here first on `interactionsSampleRate` and later again on `tracesSampleRate`
68+
// (which is done in `startInactiveSpan`). Doing it this way is easier and more bundle-size efficient.
69+
if (Math.random() > interactionsSampleRate) {
70+
return;
71+
}
72+
6573
const client = getClient();
6674
if (!client || metric.value == undefined) {
6775
return;

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ export interface BrowserTracingOptions {
103103
*/
104104
enableInp: boolean;
105105

106+
/**
107+
* Sample rate to determine interaction (INP) span sampling.
108+
*
109+
* The `interactionsSampleRate` is applied on top of the global `tracesSampleRate`.
110+
* For example, a tracesSampleRate of 0.1 and interactionsSampleRate of 0.5 will result in a 0.05 sample rate
111+
* for interactions.
112+
*
113+
* Default: 1
114+
*/
115+
interactionsSampleRate: number;
116+
106117
/**
107118
* Flag to disable patching all together for fetch requests.
108119
*
@@ -155,6 +166,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
155166
markBackgroundSpan: true,
156167
enableLongTask: true,
157168
enableInp: true,
169+
interactionsSampleRate: 1,
158170
_experiments: {},
159171
...defaultRequestInstrumentationOptions,
160172
};
@@ -173,6 +185,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
173185

174186
const {
175187
enableInp,
188+
interactionsSampleRate,
176189
enableLongTask,
177190
_experiments: { enableInteractions },
178191
beforeStartSpan,
@@ -194,7 +207,14 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
194207
const _collectWebVitals = startTrackingWebVitals();
195208

196209
if (enableInp) {
197-
startTrackingINP();
210+
const isValidInteractionsSampleRate = interactionsSampleRate >= 0 && interactionsSampleRate <= 1;
211+
if (isValidInteractionsSampleRate) {
212+
DEBUG_BUILD &&
213+
logger.warn(
214+
`[Tracing] \`interactionsSampleRate\` must be between 0 and 1. Got: ${interactionsSampleRate}. Setting to 100%`,
215+
);
216+
}
217+
startTrackingINP(isValidInteractionsSampleRate ? interactionsSampleRate : 1);
198218
}
199219

200220
if (enableLongTask) {

packages/browser/test/unit/tracing/browserTracingIntegration.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {
3636
} from '../../../src/tracing/browserTracingIntegration';
3737
import { getDefaultBrowserClientOptions } from '../helper/browser-client-options';
3838

39+
import * as browserUtils from '@sentry-internal/browser-utils';
40+
3941
// We're setting up JSDom here because the Next.js routing instrumentations requires a few things to be present on pageload:
4042
// 1. Access to window.document API for `window.document.getElementById`
4143
// 2. Access to window.location API for `window.location.pathname`
@@ -998,6 +1000,28 @@ describe('browserTracingIntegration', () => {
9981000
});
9991001
});
10001002

1003+
describe('INP - interactionsSampleRate', () => {
1004+
const startTrackingInpSpy = jest.spyOn(browserUtils, 'startTrackingINP');
1005+
1006+
it('sets interactionsSampleRate to 1 by default', () => {
1007+
browserTracingIntegration();
1008+
expect(startTrackingInpSpy).toHaveBeenCalledWith(1);
1009+
});
1010+
1011+
it.each([0, 0.5, 1])('passes on user-defined interactionsSampleRate', interactionsSampleRate => {
1012+
browserTracingIntegration({ interactionsSampleRate });
1013+
expect(startTrackingInpSpy).toHaveBeenCalledWith(interactionsSampleRate);
1014+
});
1015+
1016+
it.each([-1, 1.1, NaN, Infinity])(
1017+
'falls back to 100% when receiving an invalid interactionsSampleRate',
1018+
interactionsSampleRate => {
1019+
browserTracingIntegration({ interactionsSampleRate });
1020+
expect(startTrackingInpSpy).toHaveBeenCalledWith(1);
1021+
},
1022+
);
1023+
});
1024+
10011025
// TODO(lforst): I cannot manage to get this test to pass.
10021026
/*
10031027
it('heartbeatInterval can be a custom value', () => {

0 commit comments

Comments
 (0)