Skip to content

Commit

Permalink
feat(shared): Introduce event-specific sampling rates (#2487)
Browse files Browse the repository at this point in the history
  • Loading branch information
BRKalow committed Jan 7, 2024
1 parent 55f955f commit ea49336
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-sheep-warn.md
@@ -0,0 +1,5 @@
---
'@clerk/shared': patch
---

Update TelemetryCollector to consider event-specific sampling rates.
18 changes: 18 additions & 0 deletions packages/shared/src/__tests__/telemetry.test.ts
Expand Up @@ -152,4 +152,22 @@ describe('TelemetryCollector', () => {

fetchSpy.mockRestore();
});

test('does not send events if the random seed does not exceed the event-specific sampling rate', async () => {
const fetchSpy = jest.spyOn(global, 'fetch');
const randomSpy = jest.spyOn(Math, 'random').mockReturnValue(0.1);

const collector = new TelemetryCollector({
publishableKey: TEST_PK,
});

collector.record({ event: 'TEST_EVENT', eventSamplingRate: 0.01, payload: {} });

jest.runAllTimers();

expect(fetchSpy).not.toHaveBeenCalled();

fetchSpy.mockRestore();
randomSpy.mockRestore;
});
});
15 changes: 10 additions & 5 deletions packages/shared/src/telemetry/collector.ts
Expand Up @@ -14,7 +14,7 @@ import type { InstanceType } from '@clerk/types';

import { parsePublishableKey } from '../keys';
import { isTruthy } from '../underscore';
import type { TelemetryCollectorOptions, TelemetryEvent } from './types';
import type { TelemetryCollectorOptions, TelemetryEvent, TelemetryEventRaw } from './types';

type TelemetryCollectorConfig = Pick<
TelemetryCollectorOptions,
Expand Down Expand Up @@ -105,12 +105,12 @@ export class TelemetryCollector {
return this.#config.debug || (typeof process !== 'undefined' && isTruthy(process.env.CLERK_TELEMETRY_DEBUG));
}

record(event: Pick<TelemetryEvent, 'event' | 'payload'>): void {
record(event: TelemetryEventRaw): void {
const preparedPayload = this.#preparePayload(event.event, event.payload);

this.#logEvent(preparedPayload.event, preparedPayload);

if (!this.#shouldRecord()) {
if (!this.#shouldRecord(event.eventSamplingRate)) {
return;
}

Expand All @@ -119,8 +119,13 @@ export class TelemetryCollector {
this.#scheduleFlush();
}

#shouldRecord(): boolean {
return this.isEnabled && !this.isDebug && Math.random() <= this.#config.samplingRate;
#shouldRecord(eventSamplingRate?: number): boolean {
const randomSeed = Math.random();
const shouldBeSampled =
randomSeed <= this.#config.samplingRate &&
(typeof eventSamplingRate === 'undefined' || randomSeed <= eventSamplingRate);

return this.isEnabled && !this.isDebug && shouldBeSampled;
}

#scheduleFlush(): void {
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/telemetry/events/component-mounted.ts
@@ -1,6 +1,7 @@
import type { TelemetryEventRaw } from '../types';

const EVENT_COMPONENT_MOUNTED = 'COMPONENT_MOUNTED' as const;
const EVENT_SAMPLING_RATE = 0.1;

type EventComponentMounted = {
component: string;
Expand All @@ -19,6 +20,7 @@ export function eventComponentMounted(
): TelemetryEventRaw<EventComponentMounted> {
return {
event: EVENT_COMPONENT_MOUNTED,
eventSamplingRate: EVENT_SAMPLING_RATE,
payload: {
component,
appearanceProp: Boolean(props?.appearance),
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/telemetry/types.ts
Expand Up @@ -70,5 +70,6 @@ export type TelemetryEvent = {

export type TelemetryEventRaw<Payload = TelemetryEvent['payload']> = {
event: TelemetryEvent['event'];
eventSamplingRate?: number;
payload: Payload;
};

0 comments on commit ea49336

Please sign in to comment.