Skip to content
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

✨ [RUM-6581] Add an init parameter to chose feature flags event collection #3198

Closed
wants to merge 13 commits into from
30 changes: 29 additions & 1 deletion packages/core/src/domain/telemetry/telemetryEvent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,13 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & {
*/
use_secure_session_cookie?: boolean
/**
* Whether it is allowed to use LocalStorage when cookies are not available
* Whether it is allowed to use LocalStorage when cookies are not available (deprecated in favor of session_persistence)
*/
allow_fallback_to_local_storage?: boolean
/**
* Configure the storage strategy for persisting sessions
*/
session_persistence?: 'local-storage' | 'cookie'
/**
* Whether contexts are stored in local storage
*/
Expand Down Expand Up @@ -383,6 +387,18 @@ export type TelemetryConfigurationEvent = CommonTelemetryProperties & {
name: string
[k: string]: unknown
}[]
/**
* Whether the SDK is initialised on the application's main or a secondary process
*/
is_main_process?: boolean
/**
* The list of events that include feature flags collection. The tracking is always enabled for views and errors.
*/
track_feature_flags_for_events?: ('vital' | 'resource' | 'action' | 'long_task')[]
/**
* Whether the anonymous users are tracked
*/
track_anonymous_user?: boolean
[k: string]: unknown
}
[k: string]: unknown
Expand Down Expand Up @@ -417,6 +433,7 @@ export type TelemetryCommonFeaturesUsage =
| AddError
| SetGlobalContext
| SetUser
| SetAccount
| AddFeatureFlagEvaluation
/**
* Schema of browser specific features usage
Expand Down Expand Up @@ -505,6 +522,10 @@ export interface CommonTelemetryProperties {
id: string
[k: string]: unknown
}
/**
* The actual percentage of telemetry usage per event
*/
effective_sample_rate?: number
/**
* Enabled experimental features
*/
Expand Down Expand Up @@ -603,6 +624,13 @@ export interface SetUser {
feature: 'set-user'
[k: string]: unknown
}
export interface SetAccount {
/**
* setAccount, setAccountProperty APIs
*/
feature: 'set-account'
[k: string]: unknown
}
export interface AddFeatureFlagEvaluation {
/**
* addFeatureFlagEvaluation API
Expand Down
7 changes: 6 additions & 1 deletion packages/rum-core/src/boot/startRum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ function startRumStub(
pageStateHistory,
locationChangeObservable,
domMutationObservable,
startFeatureFlagContexts(lifeCycle, createCustomerDataTracker(noop)),
() => ({
context: {},
user: {},
Expand All @@ -92,7 +93,11 @@ function startRumStub(
noopRecorderApi
)

startLongTaskCollection(lifeCycle, configuration)
startLongTaskCollection(
lifeCycle,
configuration,
startFeatureFlagContexts(lifeCycle, createCustomerDataTracker(noop))
)
return {
stop: () => {
rumEventCollectionStop()
Expand Down
23 changes: 19 additions & 4 deletions packages/rum-core/src/boot/startRum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { createLocationChangeObservable } from '../browser/locationChangeObserva
import type { RumConfiguration } from '../domain/configuration'
import type { ViewOptions } from '../domain/view/trackViews'
import { startFeatureFlagContexts } from '../domain/contexts/featureFlagContext'
import type { FeatureFlagContexts } from '../domain/contexts/featureFlagContext'
import { startCustomerDataTelemetry } from '../domain/startCustomerDataTelemetry'
import type { PageStateHistory } from '../domain/contexts/pageStateHistory'
import { startPageStateHistory } from '../domain/contexts/pageStateHistory'
Expand Down Expand Up @@ -141,6 +142,7 @@ export function startRum(
pageStateHistory,
locationChangeObservable,
domMutationObservable,
featureFlagContexts,
getCommonContext,
reportError
)
Expand Down Expand Up @@ -168,7 +170,12 @@ export function startRum(
)
cleanupTasks.push(stopViewCollection)

const { stop: stopResourceCollection } = startResourceCollection(lifeCycle, configuration, pageStateHistory)
const { stop: stopResourceCollection } = startResourceCollection(
lifeCycle,
configuration,
pageStateHistory,
featureFlagContexts
)
cleanupTasks.push(stopResourceCollection)

if (isExperimentalFeatureEnabled(ExperimentalFeature.LONG_ANIMATION_FRAME)) {
Expand All @@ -177,14 +184,20 @@ export function startRum(
cleanupTasks.push(stopLongAnimationFrameCollection)
}
} else {
startLongTaskCollection(lifeCycle, configuration)
startLongTaskCollection(lifeCycle, configuration, featureFlagContexts)
}

const { addError } = startErrorCollection(lifeCycle, configuration, pageStateHistory, featureFlagContexts)

startRequestCollection(lifeCycle, configuration, session)

const vitalCollection = startVitalCollection(lifeCycle, pageStateHistory, customVitalsState)
const vitalCollection = startVitalCollection(
lifeCycle,
pageStateHistory,
customVitalsState,
featureFlagContexts,
configuration.trackFeatureFlagsForEvents
)
const internalContext = startInternalContext(
configuration.applicationId,
session,
Expand Down Expand Up @@ -233,6 +246,7 @@ export function startRumEventCollection(
pageStateHistory: PageStateHistory,
locationChangeObservable: Observable<LocationChange>,
domMutationObservable: Observable<void>,
featureFlagContexts: FeatureFlagContexts,
getCommonContext: () => CommonContext,
reportError: (error: RawError) => void
) {
Expand All @@ -243,7 +257,8 @@ export function startRumEventCollection(
lifeCycle,
domMutationObservable,
configuration,
pageStateHistory
pageStateHistory,
featureFlagContexts
)

const displayContext = startDisplayContext(configuration)
Expand Down
30 changes: 28 additions & 2 deletions packages/rum-core/src/domain/action/actionCollection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import type { Duration, RelativeTime, ServerDuration, TimeStamp } from '@datadog
import { ExperimentalFeature, Observable } from '@datadog/browser-core'
import { createNewEvent, mockExperimentalFeatures } from '@datadog/browser-core/test'
import type { RawRumActionEvent, RawRumEventCollectedData } from '@datadog/browser-rum-core'
import { collectAndValidateRawRumEvents, mockPageStateHistory, mockRumConfiguration } from '../../../test'
import {
collectAndValidateRawRumEvents,
mockPageStateHistory,
mockRumConfiguration,
mockFeatureFlagContexts,
} from '../../../test'
import type { RawRumEvent } from '../../rawRumEvent.types'
import { RumEventType, ActionType } from '../../rawRumEvent.types'
import { LifeCycle, LifeCycleEventType } from '../lifeCycle'
import type { FeatureFlagContexts } from '../contexts/featureFlagContext'
import { startActionCollection } from './actionCollection'

const basePageStateHistory = mockPageStateHistory({ wasInPageStateAt: () => true })
const partialFeatureFlagContexts: Partial<FeatureFlagContexts> = {}
const featureFlagContexts = mockFeatureFlagContexts(partialFeatureFlagContexts)

describe('actionCollection', () => {
const lifeCycle = new LifeCycle()
Expand All @@ -22,7 +30,8 @@ describe('actionCollection', () => {
lifeCycle,
domMutationObservable,
mockRumConfiguration(),
basePageStateHistory
basePageStateHistory,
featureFlagContexts
))

rawRumEvents = collectAndValidateRawRumEvents(lifeCycle)
Expand Down Expand Up @@ -158,4 +167,21 @@ describe('actionCollection', () => {
handlingStack: 'Error\n at foo\n at bar',
})
})
it('should include feature flags', () => {
spyOn(featureFlagContexts, 'findFeatureFlagEvaluations').and.returnValue({
'my-feature': 'enabled',
})

addAction({
name: 'foo',
startClocks: { relative: 1234 as RelativeTime, timeStamp: 123456789 as TimeStamp },
type: ActionType.CUSTOM,
})

expect(rawRumEvents.length).toBe(1)
const event = rawRumEvents[0].rawRumEvent as RawRumActionEvent
expect(event.feature_flags).toEqual({
'my-feature': 'enabled',
})
})
})
24 changes: 20 additions & 4 deletions packages/rum-core/src/domain/action/actionCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import type { RawRumActionEvent } from '../../rawRumEvent.types'
import { ActionType, RumEventType } from '../../rawRumEvent.types'
import type { LifeCycle, RawRumEventCollectedData } from '../lifeCycle'
import { LifeCycleEventType } from '../lifeCycle'
import { featureFlagCollection } from '../collectFeatureFlags'
import type { RumConfiguration } from '../configuration'
import type { CommonContext } from '../contexts/commonContext'
import type { PageStateHistory } from '../contexts/pageStateHistory'
import { PageState } from '../contexts/pageStateHistory'
import type { RumActionEventDomainContext } from '../../domainContext.types'
import type { FeatureFlagContexts } from '../contexts/featureFlagContext'
import type { ActionContexts, ClickAction } from './trackClickActions'
import { trackClickActions } from './trackClickActions'

Expand All @@ -38,10 +40,14 @@ export function startActionCollection(
lifeCycle: LifeCycle,
domMutationObservable: Observable<void>,
configuration: RumConfiguration,
pageStateHistory: PageStateHistory
pageStateHistory: PageStateHistory,
featureFlagContexts: FeatureFlagContexts
) {
lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_COMPLETED, (action) =>
lifeCycle.notify(LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processAction(action, pageStateHistory))
lifeCycle.notify(
LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
processAction(action, pageStateHistory, configuration, featureFlagContexts)
)
)

let actionContexts: ActionContexts = { findActionId: noop as () => undefined }
Expand All @@ -57,7 +63,7 @@ export function startActionCollection(
{
savedCommonContext,
},
processAction(action, pageStateHistory)
processAction(action, pageStateHistory, configuration, featureFlagContexts)
)
)
},
Expand All @@ -67,7 +73,9 @@ export function startActionCollection(

function processAction(
action: AutoAction | CustomAction,
pageStateHistory: PageStateHistory
pageStateHistory: PageStateHistory,
configuration: RumConfiguration,
featureFlagContexts: FeatureFlagContexts
): RawRumEventCollectedData<RawRumActionEvent> {
const autoActionProperties = isAutoAction(action)
? {
Expand Down Expand Up @@ -121,6 +129,14 @@ function processAction(
domainContext.handlingStack = action.handlingStack
}

featureFlagCollection(
'action',
action.startClocks.relative,
configuration.trackFeatureFlagsForEvents,
featureFlagContexts,
actionEvent
)

return {
customerContext,
rawRumEvent: actionEvent,
Expand Down
19 changes: 19 additions & 0 deletions packages/rum-core/src/domain/collectFeatureFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Context, RelativeTime } from '@datadog/browser-core'
import { includes, isEmptyObject } from '@datadog/browser-core'
import type { FeatureFlagsForEvents } from './configuration'
import type { FeatureFlagContexts } from './contexts/featureFlagContext'

export function featureFlagCollection(
eventType: FeatureFlagsForEvents,
eventStartTime: RelativeTime,
trackFeatureFlagsForEvents: FeatureFlagsForEvents[],
featureFlagContexts: FeatureFlagContexts,
rawRumEvent: { feature_flags?: Context }
) {
if (includes(trackFeatureFlagsForEvents, eventType)) {
const featureFlagContext = featureFlagContexts.findFeatureFlagEvaluations(eventStartTime)
if (featureFlagContext && !isEmptyObject(featureFlagContext)) {
rawRumEvent.feature_flags = featureFlagContext
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,33 @@ describe('validateAndBuildRumConfiguration', () => {
expect(configuration!.plugins).toEqual([plugin])
})
})
})
describe('trackFeatureFlagsForEvents', () => {
it('defaults to an empty set', () => {
const configuration = validateAndBuildRumConfiguration(DEFAULT_INIT_CONFIGURATION)!
expect(configuration.trackFeatureFlagsForEvents).toEqual([])
})

it('accepts valid event types', () => {
const configuration = validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
trackFeatureFlagsForEvents: ['resource', 'long_task', 'vital'],
})!
expect(configuration.trackFeatureFlagsForEvents).toEqual(['resource', 'long_task', 'vital'])
expect(displayWarnSpy).not.toHaveBeenCalled()
})

it('ignores invalid event types and displays a warning', () => {
const configuration = validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
trackFeatureFlagsForEvents: ['invalid_event' as any],
})!
expect(configuration.trackFeatureFlagsForEvents).toEqual([])
expect(displayWarnSpy).toHaveBeenCalledOnceWith(
"Unknown event type 'invalid_event' in trackFeatureFlagsForEvents configuration."
)
})
})
})
describe('serializeRumConfiguration', () => {
it('should serialize the configuration', () => {
const exhaustiveRumInitConfiguration: Required<RumInitConfiguration> = {
Expand All @@ -487,6 +512,7 @@ describe('serializeRumConfiguration', () => {
trackLongTasks: true,
remoteConfigurationId: '123',
plugins: [{ name: 'foo', getConfigurationTelemetry: () => ({ bar: true }) }],
trackFeatureFlagsForEvents: ['vital'],
}

type MapRumInitConfigurationKey<Key extends string> = Key extends keyof InitConfiguration
Expand Down Expand Up @@ -524,6 +550,7 @@ describe('serializeRumConfiguration', () => {
use_worker_url: true,
compress_intake_requests: true,
plugins: [{ name: 'foo', bar: true }],
track_feature_flags_for_events: ['vital'],
})
})
})
Loading