Skip to content

Commit

Permalink
feat(browser): Add interactionsSampleRate to `browserTracingIntegra…
Browse files Browse the repository at this point in the history
…tion` 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.
  • Loading branch information
Lms24 committed May 14, 2024
1 parent 012b2c4 commit c2ae9bd
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 4 deletions.
14 changes: 11 additions & 3 deletions packages/browser-utils/src/metrics/inp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import { getBrowserPerformanceAPI, msToSec } from './utils';
/**
* Start tracking INP webvital events.
*/
export function startTrackingINP(): () => void {
export function startTrackingINP(interactionsSampleRate: number): () => void {
const performance = getBrowserPerformanceAPI();
if (performance && browserPerformanceTimeOrigin) {
const inpCallback = _trackINP();
const inpCallback = _trackINP(interactionsSampleRate);

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

/** Starts tracking the Interaction to Next Paint on the current page. */
function _trackINP(): () => void {
function _trackINP(interactionsSampleRate: number): () => void {
return addInpInstrumentationHandler(({ metric }) => {
// As specified in the `interactionsSampleRate` option, the sampling decision shall be based on
// `tracesSampleRate` x `interactionsSampleRate`
// This is the same as sampling here first on `interactionsSampleRate` and later again on `tracesSampleRate`
// (which is done in `startInactiveSpan`). Doing it this way is easier and more bundle-size efficient.
if (Math.random() > interactionsSampleRate) {
return;
}

const client = getClient();
if (!client || metric.value == undefined) {
return;
Expand Down
22 changes: 21 additions & 1 deletion packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ export interface BrowserTracingOptions {
*/
enableInp: boolean;

/**
* Sample rate to determine interaction (INP) span sampling.
*
* The `interactionsSampleRate` is applied on top of the global `tracesSampleRate`.
* For example, a tracesSampleRate of 0.1 and interactionsSampleRate of 0.5 will result in a 0.05 sample rate
* for interactions.
*
* Default: 1
*/
interactionsSampleRate: number;

/**
* Flag to disable patching all together for fetch requests.
*
Expand Down Expand Up @@ -155,6 +166,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
markBackgroundSpan: true,
enableLongTask: true,
enableInp: true,
interactionsSampleRate: 1,
_experiments: {},
...defaultRequestInstrumentationOptions,
};
Expand All @@ -173,6 +185,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio

const {
enableInp,
interactionsSampleRate,
enableLongTask,
_experiments: { enableInteractions },
beforeStartSpan,
Expand All @@ -194,7 +207,14 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
const _collectWebVitals = startTrackingWebVitals();

if (enableInp) {
startTrackingINP();
const isValidInteractionsSampleRate = interactionsSampleRate >= 0 && interactionsSampleRate <= 1;
if (isValidInteractionsSampleRate) {
DEBUG_BUILD &&
logger.warn(
`[Tracing] \`interactionsSampleRate\` must be between 0 and 1. Got: ${interactionsSampleRate}. Setting to 100%`,
);
}
startTrackingINP(isValidInteractionsSampleRate ? interactionsSampleRate : 1);
}

if (enableLongTask) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import {
} from '../../../src/tracing/browserTracingIntegration';
import { getDefaultBrowserClientOptions } from '../helper/browser-client-options';

import * as browserUtils from '@sentry-internal/browser-utils';

// We're setting up JSDom here because the Next.js routing instrumentations requires a few things to be present on pageload:
// 1. Access to window.document API for `window.document.getElementById`
// 2. Access to window.location API for `window.location.pathname`
Expand Down Expand Up @@ -998,6 +1000,28 @@ describe('browserTracingIntegration', () => {
});
});

describe('INP - interactionsSampleRate', () => {
const startTrackingInpSpy = jest.spyOn(browserUtils, 'startTrackingINP');

it('sets interactionsSampleRate to 1 by default', () => {
browserTracingIntegration();
expect(startTrackingInpSpy).toHaveBeenCalledWith(1);
});

it.each([0, 0.5, 1])('passes on user-defined interactionsSampleRate', interactionsSampleRate => {
browserTracingIntegration({ interactionsSampleRate });
expect(startTrackingInpSpy).toHaveBeenCalledWith(interactionsSampleRate);
});

it.each([-1, 1.1, NaN, Infinity])(
'falls back to 100% when receiving an invalid interactionsSampleRate',
interactionsSampleRate => {
browserTracingIntegration({ interactionsSampleRate });
expect(startTrackingInpSpy).toHaveBeenCalledWith(1);
},
);
});

// TODO(lforst): I cannot manage to get this test to pass.
/*
it('heartbeatInterval can be a custom value', () => {
Expand Down

0 comments on commit c2ae9bd

Please sign in to comment.