Skip to content

Commit

Permalink
🔊 Collect configuration telemetry event (#1760)
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque authored Oct 12, 2022
1 parent 369826b commit 124285f
Show file tree
Hide file tree
Showing 24 changed files with 431 additions and 57 deletions.
26 changes: 26 additions & 0 deletions packages/core/src/domain/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getCurrentSite } from '../../browser/cookie'
import { catchUserErrors } from '../../tools/catchUserErrors'
import { display } from '../../tools/display'
import { assign, isPercentage, ONE_KIBI_BYTE, ONE_SECOND } from '../../tools/utils'
import type { RawTelemetryConfiguration } from '../telemetry'
import { updateExperimentalFeatures } from './experimentalFeatures'
import { initSimulation } from './simulation'
import type { TransportConfiguration } from './transportConfiguration'
Expand Down Expand Up @@ -41,6 +42,7 @@ export interface InitConfiguration {
enableExperimentalFeatures?: string[] | undefined
replica?: ReplicaUserConfiguration | undefined
datacenter?: string
telemetryConfigurationSampleRate?: number

// simulation options
simulationStart?: string | undefined
Expand All @@ -63,6 +65,7 @@ export interface Configuration extends TransportConfiguration {
cookieOptions: CookieOptions
sampleRate: number
telemetrySampleRate: number
telemetryConfigurationSampleRate: number
service: string | undefined
silentMultipleInit: boolean

Expand Down Expand Up @@ -93,6 +96,14 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati
return
}

if (
initConfiguration.telemetryConfigurationSampleRate !== undefined &&
!isPercentage(initConfiguration.telemetryConfigurationSampleRate)
) {
display.error('Telemetry Configuration Sample Rate should be a number between 0 and 100')
return
}

// Set the experimental feature flags as early as possible, so we can use them in most places
updateExperimentalFeatures(initConfiguration.enableExperimentalFeatures)

Expand All @@ -105,6 +116,7 @@ export function validateAndBuildConfiguration(initConfiguration: InitConfigurati
cookieOptions: buildCookieOptions(initConfiguration),
sampleRate: initConfiguration.sampleRate ?? 100,
telemetrySampleRate: initConfiguration.telemetrySampleRate ?? 20,
telemetryConfigurationSampleRate: initConfiguration.telemetryConfigurationSampleRate ?? 5,
service: initConfiguration.service,
silentMultipleInit: !!initConfiguration.silentMultipleInit,

Expand Down Expand Up @@ -149,3 +161,17 @@ export function buildCookieOptions(initConfiguration: InitConfiguration) {
function mustUseSecureCookie(initConfiguration: InitConfiguration) {
return !!initConfiguration.useSecureSessionCookie || !!initConfiguration.useCrossSiteSessionCookie
}

export function serializeConfiguration(configuration: InitConfiguration): Partial<RawTelemetryConfiguration> {
return {
session_sample_rate: configuration.sampleRate,
telemetry_sample_rate: configuration.telemetrySampleRate,
telemetry_configuration_sample_rate: configuration.telemetryConfigurationSampleRate,
use_before_send: !!configuration.beforeSend,
use_cross_site_session_cookie: configuration.useCrossSiteSessionCookie,
use_secure_session_cookie: configuration.useSecureSessionCookie,
use_proxy: configuration.proxyUrl !== undefined ? !!configuration.proxyUrl : undefined,
silent_multiple_init: configuration.silentMultipleInit,
track_session_across_subdomains: configuration.trackSessionAcrossSubdomains,
}
}
1 change: 1 addition & 0 deletions packages/core/src/domain/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {
buildCookieOptions,
DefaultPrivacyLevel,
validateAndBuildConfiguration,
serializeConfiguration,
} from './configuration'
export { createEndpointBuilder, EndpointBuilder, EndpointType } from './endpointBuilder'
export {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/domain/telemetry/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export {
Telemetry,
RawTelemetryEvent,
addTelemetryDebug,
addTelemetryError,
startFakeTelemetry,
resetTelemetry,
startTelemetry,
isTelemetryReplicationAllowed,
addTelemetryConfiguration,
} from './telemetry'

export * from './rawTelemetryEvent.types'
export * from './telemetryEvent.types'
14 changes: 14 additions & 0 deletions packages/core/src/domain/telemetry/rawTelemetryEvent.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { TelemetryEvent, TelemetryConfigurationEvent } from './telemetryEvent.types'

export const TelemetryType = {
log: 'log',
configuration: 'configuration',
} as const

export const enum StatusType {
debug = 'debug',
error = 'error',
}

export type RawTelemetryEvent = TelemetryEvent['telemetry']
export type RawTelemetryConfiguration = TelemetryConfigurationEvent['telemetry']['configuration']
57 changes: 55 additions & 2 deletions packages/core/src/domain/telemetry/telemetry.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import type { StackTrace } from '@datadog/browser-core'
import { callMonitored } from '../../tools/monitor'
import type { Configuration } from '../configuration'
import { updateExperimentalFeatures, INTAKE_SITE_US1, INTAKE_SITE_US1_FED } from '../configuration'
import { resetTelemetry, startTelemetry, scrubCustomerFrames, formatError } from './telemetry'
import {
resetExperimentalFeatures,
updateExperimentalFeatures,
INTAKE_SITE_US1,
INTAKE_SITE_US1_FED,
} from '../configuration'
import {
resetTelemetry,
startTelemetry,
scrubCustomerFrames,
formatError,
addTelemetryConfiguration,
} from './telemetry'

function startAndSpyTelemetry(configuration?: Partial<Configuration>) {
const telemetry = startTelemetry({
Expand Down Expand Up @@ -31,6 +42,48 @@ describe('telemetry', () => {
expect(notifySpy).toHaveBeenCalledTimes(1)
})

describe('addTelemetryConfiguration', () => {
afterEach(() => {
resetExperimentalFeatures()
})

it('should collects configuration when sampled and ff is enabled', () => {
updateExperimentalFeatures(['telemetry_configuration'])

const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 100, telemetryConfigurationSampleRate: 100 })

addTelemetryConfiguration({})

expect(notifySpy).toHaveBeenCalled()
})

it('should not notify configuration when sampled and ff is disabled', () => {
const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 100, telemetryConfigurationSampleRate: 100 })

addTelemetryConfiguration({})

expect(notifySpy).not.toHaveBeenCalled()
})

it('should not notify configuration when not sampled and ff is enabled', () => {
updateExperimentalFeatures(['telemetry_configuration'])
const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 100, telemetryConfigurationSampleRate: 0 })

addTelemetryConfiguration({})

expect(notifySpy).not.toHaveBeenCalled()
})

it('should not notify configuration when telemetrySampleRate is 0 and ff is enabled', () => {
updateExperimentalFeatures(['telemetry_configuration'])
const { notifySpy } = startAndSpyTelemetry({ telemetrySampleRate: 0, telemetryConfigurationSampleRate: 100 })

addTelemetryConfiguration({})

expect(notifySpy).not.toHaveBeenCalled()
})
})

it('should contains feature flags', () => {
updateExperimentalFeatures(['foo'])
const { notifySpy } = startAndSpyTelemetry()
Expand Down
33 changes: 18 additions & 15 deletions packages/core/src/domain/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { toStackTraceString } from '../../tools/error'
import { assign, combine, jsonStringify, performDraw, includes, startsWith, arrayFrom } from '../../tools/utils'
import type { Configuration } from '../configuration'
import {
isExperimentalFeatureEnabled,
getExperimentalFeatures,
getSimulationLabel,
INTAKE_SITE_STAGING,
Expand All @@ -16,15 +17,12 @@ import { Observable } from '../../tools/observable'
import { timeStampNow } from '../../tools/timeUtils'
import { displayIfDebugEnabled, startMonitorErrorCollection } from '../../tools/monitor'
import type { TelemetryEvent } from './telemetryEvent.types'
import type { RawTelemetryConfiguration, RawTelemetryEvent } from './rawTelemetryEvent.types'
import { StatusType, TelemetryType } from './rawTelemetryEvent.types'

// replaced at build time
declare const __BUILD_ENV__SDK_VERSION__: string

const enum StatusType {
debug = 'debug',
error = 'error',
}

const ALLOWED_FRAME_URLS = [
'https://www.datadoghq-browser-agent.com',
'https://www.datad0g-browser-agent.com',
Expand All @@ -37,22 +35,14 @@ export interface Telemetry {
observable: Observable<TelemetryEvent & Context>
}

export interface RawTelemetryEvent extends Context {
message: string
status: StatusType
error?: {
kind?: string
stack: string
}
}

const TELEMETRY_EXCLUDED_SITES: string[] = [INTAKE_SITE_US1_FED]

const telemetryConfiguration: {
maxEventsPerPage: number
sentEventCount: number
telemetryEnabled: boolean
} = { maxEventsPerPage: 0, sentEventCount: 0, telemetryEnabled: false }
telemetryConfigurationEnabled: boolean
} = { maxEventsPerPage: 0, sentEventCount: 0, telemetryEnabled: false, telemetryConfigurationEnabled: false }

let onRawTelemetryEventCollected: ((event: RawTelemetryEvent) => void) | undefined

Expand All @@ -61,6 +51,8 @@ export function startTelemetry(configuration: Configuration): Telemetry {
const observable = new Observable<TelemetryEvent & Context>()

telemetryConfiguration.telemetryEnabled = performDraw(configuration.telemetrySampleRate)
telemetryConfiguration.telemetryConfigurationEnabled =
telemetryConfiguration.telemetryEnabled && performDraw(configuration.telemetryConfigurationSampleRate)

onRawTelemetryEventCollected = (event: RawTelemetryEvent) => {
if (!includes(TELEMETRY_EXCLUDED_SITES, configuration.site) && telemetryConfiguration.telemetryEnabled) {
Expand Down Expand Up @@ -132,6 +124,7 @@ export function addTelemetryDebug(message: string, context?: Context) {
addTelemetry(
assign(
{
type: TelemetryType.log,
message,
status: StatusType.debug,
},
Expand All @@ -144,13 +137,23 @@ export function addTelemetryError(e: unknown) {
addTelemetry(
assign(
{
type: TelemetryType.log,
status: StatusType.error,
},
formatError(e)
)
)
}

export function addTelemetryConfiguration(configuration: RawTelemetryConfiguration) {
if (isExperimentalFeatureEnabled('telemetry_configuration') && telemetryConfiguration.telemetryConfigurationEnabled) {
addTelemetry({
type: TelemetryType.configuration,
configuration,
})
}
}

function addTelemetry(event: RawTelemetryEvent) {
if (onRawTelemetryEventCollected && telemetryConfiguration.sentEventCount < telemetryConfiguration.maxEventsPerPage) {
telemetryConfiguration.sentEventCount += 1
Expand Down
Loading

0 comments on commit 124285f

Please sign in to comment.