diff --git a/benchmarks/src/testSetup/testUtils.ts b/benchmarks/src/testSetup/testUtils.ts index 2bd28f913..5fb3c0943 100644 --- a/benchmarks/src/testSetup/testUtils.ts +++ b/benchmarks/src/testSetup/testUtils.ts @@ -9,7 +9,6 @@ import { DatadogProviderConfiguration, DdSdkReactNative, CoreConfiguration, - RumConfiguration, SdkVerbosity, TrackingConsent } from '@datadog/mobile-react-native'; @@ -41,14 +40,25 @@ export const getDatadogProviderConfig = () => { let config = new DatadogProviderConfiguration( baseConfig.clientToken ?? '', baseConfig.env ?? '', - TrackingConsent.GRANTED + TrackingConsent.GRANTED, + { + rumConfiguration: { + applicationId: baseConfig.applicationID ?? '', + trackInteractions: true, + trackResources: true, + trackErrors: true, + sessionSampleRate: 100, + nativeCrashReportEnabled: true + }, + logsConfiguration: { + bundleLogsWithRum: true, + bundleLogsWithTraces: true, + }, + traceConfiguration: {} + } ); config.service = `com.rn.${platform}.benchmark` config.verbosity = SdkVerbosity.DEBUG; - config.nativeCrashReportEnabled = true - - config.rumConfiguration = new RumConfiguration(baseConfig.applicationID ?? '', true, true, true); - config.rumConfiguration.sessionSampleRate = 100; return config; }; @@ -58,13 +68,25 @@ export const initializeDatadog = (clientToken?: string, environment?: string, ap const config = new CoreConfiguration( clientToken ?? '', environment ?? '', - TrackingConsent.GRANTED + TrackingConsent.GRANTED, + { + rumConfiguration: { + applicationId: appId ?? "", + trackInteractions: true, + trackResources: true, + trackErrors: true, + sessionSampleRate: 100, + nativeCrashReportEnabled: true, + }, + logsConfiguration: { + bundleLogsWithRum: true, + bundleLogsWithTraces: true, + }, + traceConfiguration: {} + } ); config.service = `com.rn.${platform}.benchmark` config.verbosity = SdkVerbosity.DEBUG; - config.nativeCrashReportEnabled = true - config.rumConfiguration = new RumConfiguration(appId ?? '', true, true, true) - config.rumConfiguration.sessionSampleRate = 100; return DdSdkReactNative.initialize(config); }; diff --git a/example-new-architecture/App.tsx b/example-new-architecture/App.tsx index 20e0c3eda..3821d9a66 100644 --- a/example-new-architecture/App.tsx +++ b/example-new-architecture/App.tsx @@ -8,7 +8,7 @@ import { RumActionType, DdLogs, DdTrace, - RumConfiguration, + TrackingConsent, } from '@datadog/mobile-react-native'; import React from 'react'; import type {PropsWithChildren} from 'react'; @@ -36,18 +36,21 @@ import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials'; const config = new CoreConfiguration( CLIENT_TOKEN, ENVIRONMENT, + TrackingConsent.GRANTED, + { + rumConfiguration: { + applicationId: APPLICATION_ID, + trackInteractions: true, + trackResources: true, + trackFrustrations: true, + sessionSampleRate: 100, + telemetrySampleRate: 100 + } + } ); config.verbosity = SdkVerbosity.DEBUG; config.uploadFrequency = UploadFrequency.FREQUENT; config.batchSize = BatchSize.SMALL; - config.rumConfiguration = new RumConfiguration( - APPLICATION_ID, - true, - true, - true - ) - config.rumConfiguration.sessionSampleRate = 100; - config.rumConfiguration.telemetrySampleRate = 100; await DdSdkReactNative.initialize(config); await DdRum.startView('main', 'Main'); diff --git a/example/src/App.tsx b/example/src/App.tsx index 7a4de6a47..a62e1fac2 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -66,7 +66,6 @@ const configuration = getDatadogConfig(TrackingConsent.GRANTED) // see https://docs.datadoghq.com/real_user_monitoring/guide/initialize-your-native-sdk-before-react-native-starts // const configuration = new DatadogProviderConfiguration("fake_value", "fake_value"); -// configuration.rumConfiguration = new RumConfiguration("fake_value") export default function App() { diff --git a/example/src/ddUtils.tsx b/example/src/ddUtils.tsx index 8c75dea94..b49caadfd 100644 --- a/example/src/ddUtils.tsx +++ b/example/src/ddUtils.tsx @@ -3,12 +3,14 @@ import { DdLogs, DdSdkReactNative, CoreConfiguration, - RumConfiguration, SdkVerbosity, - TrackingConsent + TrackingConsent, + BatchSize, + UploadFrequency, } from '@datadog/mobile-react-native'; import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials'; +import { BatchProcessingLevel } from '@datadog/mobile-react-native/src/config/types'; // New SDK Setup - not available for react-native-navigation export function getDatadogConfig(trackingConsent: TrackingConsent) { @@ -16,13 +18,34 @@ export function getDatadogConfig(trackingConsent: TrackingConsent) { CLIENT_TOKEN, ENVIRONMENT, trackingConsent, + { + batchSize: BatchSize.SMALL, + uploadFrequency: UploadFrequency.FREQUENT, + batchProcessingLevel: BatchProcessingLevel.MEDIUM, + additionalConfiguration: { + customProperty: "sdk-example-app" + }, + rumConfiguration: { + applicationId: APPLICATION_ID, + trackInteractions: true, + trackResources: true, + trackErrors: true, + sessionSampleRate: 100, + nativeCrashReportEnabled: true + }, + logsConfiguration: { + logEventMapper: (logEvent) => { + logEvent.message = `[CUSTOM] ${logEvent.message}`; + return logEvent; + } + }, + traceConfiguration: {} + } ); config.service = "com.datadoghq.reactnative.sample" - config.nativeCrashReportEnabled = true config.verbosity = SdkVerbosity.DEBUG; - config.rumConfiguration = new RumConfiguration(APPLICATION_ID, true, true, true); - config.rumConfiguration.sessionSampleRate = 100 + return config } @@ -38,13 +61,21 @@ export function initializeDatadog(trackingConsent: TrackingConsent) { const config = new CoreConfiguration( CLIENT_TOKEN, ENVIRONMENT, - trackingConsent + trackingConsent, + { + rumConfiguration: { + applicationId: APPLICATION_ID, + trackInteractions: true, + trackResources: true, + trackErrors: true, + sessionSampleRate: 100, + nativeCrashReportEnabled: true + } + } ) - config.nativeCrashReportEnabled = true + config.verbosity = SdkVerbosity.DEBUG; config.service = "com.datadoghq.reactnative.sample" - config.rumConfiguration = new RumConfiguration(APPLICATION_ID, true, true, true); - config.rumConfiguration.sessionSampleRate = 100 DdSdkReactNative.initialize(config).then(() => { DdLogs.info('The RN Sdk was properly initialized') diff --git a/example/src/screens/MainScreen.tsx b/example/src/screens/MainScreen.tsx index 7f0fbdec2..53b94a180 100644 --- a/example/src/screens/MainScreen.tsx +++ b/example/src/screens/MainScreen.tsx @@ -14,7 +14,6 @@ import { APPLICATION_KEY, API_KEY } from '../../src/ddCredentials'; import { DdLogs, DdSdkReactNative, TrackingConsent } from '@datadog/mobile-react-native'; import { getTrackingConsent, saveTrackingConsent } from '../utils'; import { ConsentModal } from '../components/consent'; -import { DdRum } from '../../../packages/core/src/rum/DdRum'; const axios = require('../axiosConfig'); diff --git a/packages/codepush/src/__tests__/index.test.tsx b/packages/codepush/src/__tests__/index.test.tsx index cb2f09ac3..68a1a82f3 100644 --- a/packages/codepush/src/__tests__/index.test.tsx +++ b/packages/codepush/src/__tests__/index.test.tsx @@ -20,7 +20,7 @@ jest.mock('@datadog/mobile-react-native', () => { const flushPromises = () => new Promise(jest.requireActual('timers').setImmediate); -const createCodepushPackageMock = label => ({ +const createCodepushPackageMock = (label: string | null) => ({ label, isMandatory: false, install: jest.fn(), @@ -285,23 +285,28 @@ describe('AppCenter Codepush integration', () => { } = require('@datadog/mobile-react-native'); const autoInstrumentationConfig = { + clientToken: 'fake-client-token', + env: 'fake-env', rumConfiguration: { + applicationId: 'fake-app-id', + useAccessibilityLabel: true, actionNameAttribute: 'test-action-name-attr', trackErrors: true, trackResources: true, - trackInteractions: true + trackInteractions: true, + resourceTraceSampleRate: 100, + nativeCrashReportEnabled: true, + nativeLongTaskThresholdMs: false, + nativeViewTracking: true, + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: [PropagatorType.DATADOG] + } + ] }, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [PropagatorType.DATADOG] - } - ], - useAccessibilityLabel: true, logsConfiguration: {}, - traceConfiguration: { - resourceTraceSampleRate: 100 - } + traceConfiguration: {} }; const configuration = new FileBasedConfiguration({ @@ -320,27 +325,28 @@ describe('AppCenter Codepush integration', () => { DdSdkReactNative._enableFeaturesFromDatadogProvider ).toHaveBeenCalledWith({ rumConfiguration: { + useAccessibilityLabel: true, actionNameAttribute: 'test-action-name-attr', actionEventMapper: null, + nativeCrashReportEnabled: true, + nativeLongTaskThresholdMs: false, + nativeViewTracking: true, resourceEventMapper: null, errorEventMapper: null, trackErrors: true, trackResources: true, - trackInteractions: true + trackInteractions: true, + resourceTraceSampleRate: 100, + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: [PropagatorType.DATADOG] + } + ] }, logsConfiguration: { logEventMapper: null - }, - traceConfiguration: { - resourceTraceSampleRate: 100 - }, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [PropagatorType.DATADOG] - } - ], - useAccessibilityLabel: true + } }); expect( @@ -358,27 +364,14 @@ describe('AppCenter Codepush integration', () => { const { DatadogCodepushProvider } = require('..'); const { DdSdkReactNative, - PropagatorType, FileBasedConfiguration } = require('@datadog/mobile-react-native'); const autoInstrumentationConfig = { + clientToken: 'fake-client-token', + env: 'fake-env', rumConfiguration: { - actionNameAttribute: 'test-action-name-attr', - trackErrors: true, - trackResources: true, - trackInteractions: true - }, - logsConfiguration: {}, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [PropagatorType.DATADOG] - } - ], - useAccessibilityLabel: true, - traceConfiguration: { - resourceTraceSampleRate: 100 + applicationId: 'fake-app-id' } }; @@ -396,30 +389,25 @@ describe('AppCenter Codepush integration', () => { }); expect( DdSdkReactNative._enableFeaturesFromDatadogProvider - ).toHaveBeenCalledWith({ - rumConfiguration: { - actionNameAttribute: 'test-action-name-attr', - trackErrors: true, - trackResources: true, - trackInteractions: true, - actionEventMapper: null, - resourceEventMapper: null, - errorEventMapper: null - }, - logsConfiguration: { - logEventMapper: null - }, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [PropagatorType.DATADOG] - } - ], - traceConfiguration: { - resourceTraceSampleRate: 100 - }, - useAccessibilityLabel: true - }); + ).toHaveBeenCalledWith( + expect.objectContaining({ + rumConfiguration: expect.objectContaining({ + useAccessibilityLabel: true, + actionNameAttribute: undefined, + trackErrors: false, + trackResources: false, + trackInteractions: false, + actionEventMapper: null, + resourceEventMapper: null, + errorEventMapper: null, + resourceTraceSampleRate: 100, + firstPartyHosts: [] + }), + logsConfiguration: expect.objectContaining({ + logEventMapper: null + }) + }) + ); expect( DdSdkReactNative._enableFeaturesFromDatadogProvider diff --git a/packages/codepush/src/index.ts b/packages/codepush/src/index.ts index af554a0ab..cb1bca06c 100644 --- a/packages/codepush/src/index.ts +++ b/packages/codepush/src/index.ts @@ -44,9 +44,10 @@ const buildPartialConfiguration = ( configuration: DatadogProviderConfiguration ): AutoInstrumentationConfiguration => { const partialConfiguration: RequiredOrDiscard = { - useAccessibilityLabel: configuration.useAccessibilityLabel, - firstPartyHosts: configuration.firstPartyHosts, rumConfiguration: { + firstPartyHosts: configuration.rumConfiguration?.firstPartyHosts, + useAccessibilityLabel: + configuration.rumConfiguration?.useAccessibilityLabel ?? true, actionNameAttribute: configuration.rumConfiguration?.actionNameAttribute, trackErrors: configuration.rumConfiguration?.trackErrors ?? false, @@ -54,17 +55,26 @@ const buildPartialConfiguration = ( configuration.rumConfiguration?.trackResources ?? false, trackInteractions: configuration.rumConfiguration?.trackInteractions ?? false, - errorEventMapper: configuration.rumConfiguration?.errorEventMapper, + resourceTraceSampleRate: + configuration.rumConfiguration?.resourceTraceSampleRate ?? 100, + nativeCrashReportEnabled: + configuration.rumConfiguration?.nativeCrashReportEnabled ?? + false, + nativeLongTaskThresholdMs: + configuration.rumConfiguration?.nativeLongTaskThresholdMs ?? + 200, + nativeViewTracking: + configuration.rumConfiguration?.nativeViewTracking ?? false, + errorEventMapper: + configuration.rumConfiguration?.errorEventMapper ?? null, resourceEventMapper: - configuration.rumConfiguration?.resourceEventMapper, - actionEventMapper: configuration.rumConfiguration?.actionEventMapper + configuration.rumConfiguration?.resourceEventMapper ?? null, + actionEventMapper: + configuration.rumConfiguration?.actionEventMapper ?? null }, logsConfiguration: { - logEventMapper: configuration.logsConfiguration?.logEventMapper - }, - traceConfiguration: { - resourceTraceSampleRate: - configuration.traceConfiguration?.resourceTraceSampleRate + logEventMapper: + configuration.logsConfiguration?.logEventMapper ?? null } }; diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt index 05c7c8c4d..545cd0962 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt @@ -19,8 +19,6 @@ import java.net.Proxy * @param site The Datadog site of your organization (can be 'US', 'EU' or 'GOV', default is 'US'). * @param service Custom service name. * @param verbosity Verbosity level of the SDK’s internal logging. - * @param nativeCrashReportEnabled Whether the SDK should track native Android crashes (default is false). - * @param nativeLongTaskThresholdMs The threshold for native long tasks reporting in milliseconds. * @param trackingConsent Consent, which can take one of the following values: 'pending', 'granted', 'not_granted'. * @param uploadFrequency The frequency to which batches of data are sent (can be 'RARE', 'AVERAGE' (default), 'FREQUENT') * @param batchSize The preferred size for uploaded batches of data (can be 'SMALL', 'MEDIUM' (default), 'LARGE') @@ -39,8 +37,6 @@ data class DdSdkConfiguration( val site: String? = null, val service: String? = null, val verbosity: String? = null, - val nativeCrashReportEnabled: Boolean? = null, - val nativeLongTaskThresholdMs: Double? = null, val trackingConsent: String? = null, val uploadFrequency: String? = null, val batchSize: String? = null, @@ -60,8 +56,11 @@ data class DdSdkConfiguration( * @param trackFrustrations Whether to track frustration signals or not. * @param longTaskThresholdMs The threshold for javascript long tasks reporting in milliseconds. * @param sessionSampleRate The sample rate (between 0 and 100) of RUM sessions kept. + * @param resourceTraceSampleRate Percentage (0–100) of tracing integrations for network calls between your app and your backend. * @param vitalsUpdateFrequency The frequency to which vitals update are sent (can be 'NEVER', 'RARE', 'AVERAGE' (default), 'FREQUENT'). * @param trackBackgroundEvents Enables/Disables tracking RUM event when no RUM View is active. Might increase number of sessions and billing. + * @param nativeCrashReportEnabled Whether the SDK should track native Android crashes (default is false). + * @param nativeLongTaskThresholdMs The threshold for native long tasks reporting in milliseconds. * @param nativeViewTracking Enables/Disables tracking RUM Views on the native level. * @param nativeInteractionTracking Enables/Disables tracking RUM Actions on the native level. * @param trackNonFatalAnrs Enables tracking of non-fatal ANRs on Android. @@ -74,8 +73,11 @@ data class RumConfiguration( val trackFrustrations: Boolean? = null, val longTaskThresholdMs: Double? = null, val sessionSampleRate: Double? = null, + val resourceTraceSampleRate: Double? = null, val vitalsUpdateFrequency: String? = null, val trackBackgroundEvents: Boolean? = null, + val nativeCrashReportEnabled: Boolean? = null, + val nativeLongTaskThresholdMs: Double? = null, val nativeViewTracking: Boolean? = null, val nativeInteractionTracking: Boolean? = null, val trackNonFatalAnrs: Boolean? = null, @@ -100,12 +102,9 @@ data class LogsConfiguration( /** * A configuration object for Datadog Traces (APM) features. * - * @param resourceTraceSampleRate Percentage (0–100) of tracing integrations for network calls between your app - * and your backend. * @param customEndpoint Custom Trace intake endpoint used to override the default Datadog intake. */ data class TraceConfiguration( - val resourceTraceSampleRate: Double? = null, val customEndpoint: String? = null ) @@ -118,12 +117,9 @@ internal data class JSONDdSdkConfiguration( val clientToken: String, val env: String, val trackingConsent: String? = null, - val useAccessibilityLabel: Boolean? = null, val additionalConfiguration: Map? = null, val batchSize: String? = null, val batchProcessingLevel: String? = null, - val nativeCrashReportEnabled: Boolean? = null, - val nativeLongTaskThresholdMs: Double? = null, // we treat only numeric in JSON val proxyConfiguration: JSONProxyConfiguration? = null, val service: String? = null, val uploadFrequency: String? = null, @@ -139,6 +135,7 @@ internal data class JSONDdSdkConfiguration( internal data class JSONRumConfiguration( val applicationId: String? = null, + val useAccessibilityLabel: Boolean? = null, val trackInteractions: Boolean? = null, val trackResources: Boolean? = null, val trackErrors: Boolean? = null, @@ -148,10 +145,12 @@ internal data class JSONRumConfiguration( // schema: integer | boolean, we only support numeric in JSON file val longTaskThresholdMs: Double? = null, - + val nativeCrashReportEnabled: Boolean? = null, + val nativeLongTaskThresholdMs: Double? = null, // we treat only numeric in JSON val nativeViewTracking: Boolean? = null, val nativeInteractionTracking: Boolean? = null, val sessionSampleRate: Double? = null, + val resourceTraceSampleRate: Double? = null, val trackBackgroundEvents: Boolean? = null, val trackFrustrations: Boolean? = null, val trackNonFatalAnrs: Boolean? = null, @@ -169,7 +168,6 @@ internal data class JSONLogsConfiguration( ) internal data class JSONTraceConfiguration( - val resourceTraceSampleRate: Double? = null, val customEndpoint: String? = null ) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt index b16614719..1e4f9b979 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt @@ -34,8 +34,11 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration { trackFrustrations = rm.getBooleanOrNull("trackFrustrations"), longTaskThresholdMs = rm.getDoubleOrNull("longTaskThresholdMs") ?: 0.0, sessionSampleRate = rm.getDoubleOrNull("sessionSampleRate"), + resourceTraceSampleRate = rm.getDoubleOrNull("resourceTraceSampleRate"), vitalsUpdateFrequency = rm.getString("vitalsUpdateFrequency"), trackBackgroundEvents = rm.getBooleanOrNull("trackBackgroundEvents"), + nativeCrashReportEnabled = rm.getBooleanOrNull("nativeCrashReportEnabled"), + nativeLongTaskThresholdMs = rm.getDoubleOrNull("nativeLongTaskThresholdMs"), nativeViewTracking = rm.getBooleanOrNull("nativeViewTracking"), nativeInteractionTracking = rm.getBooleanOrNull("nativeInteractionTracking"), trackNonFatalAnrs = rm.getBooleanOrNull("trackNonFatalAnrs"), @@ -55,7 +58,6 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration { val traceConfiguration: TraceConfiguration? = traceMap?.let { tm -> TraceConfiguration( - resourceTraceSampleRate = tm.getDoubleOrNull("resourceTraceSampleRate"), customEndpoint = tm.getString("customEndpoint") ) } @@ -70,8 +72,6 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration { site = getString("site"), service = getString("service"), verbosity = getString("verbosity"), - nativeCrashReportEnabled = getBooleanOrNull("nativeCrashReportEnabled"), - nativeLongTaskThresholdMs = getDoubleOrNull("nativeLongTaskThresholdMs"), trackingConsent = getString("trackingConsent"), uploadFrequency = getString("uploadFrequency"), batchSize = getString("batchSize"), @@ -175,8 +175,11 @@ internal fun JSONDdSdkConfiguration.asDdSdkConfiguration(): DdSdkConfiguration { trackFrustrations = rum.trackFrustrations ?: DefaultConfiguration.trackFrustrations, longTaskThresholdMs = rum.longTaskThresholdMs ?: DefaultConfiguration.longTaskThresholdMs, sessionSampleRate = rum.sessionSampleRate ?: DefaultConfiguration.sessionSamplingRate, + resourceTraceSampleRate = rum.resourceTraceSampleRate, vitalsUpdateFrequency = rum.vitalsUpdateFrequency ?: DefaultConfiguration.vitalsUpdateFrequency, trackBackgroundEvents = rum.trackBackgroundEvents ?: DefaultConfiguration.trackBackgroundEvents, + nativeCrashReportEnabled = rum.nativeCrashReportEnabled ?: DefaultConfiguration.nativeCrashReportEnabled, + nativeLongTaskThresholdMs = rum.nativeLongTaskThresholdMs ?: DefaultConfiguration.nativeLongTaskThresholdMs, nativeViewTracking = rum.nativeViewTracking ?: DefaultConfiguration.nativeViewTracking, nativeInteractionTracking = rum.nativeInteractionTracking ?: DefaultConfiguration.nativeInteractionTracking, trackNonFatalAnrs = rum.trackNonFatalAnrs, @@ -197,7 +200,6 @@ internal fun JSONDdSdkConfiguration.asDdSdkConfiguration(): DdSdkConfiguration { val traceConfiguration: TraceConfiguration? = this.traceConfiguration?.let { trace -> TraceConfiguration( - resourceTraceSampleRate = trace.resourceTraceSampleRate, customEndpoint = trace.customEndpoint ) } @@ -213,8 +215,6 @@ internal fun JSONDdSdkConfiguration.asDdSdkConfiguration(): DdSdkConfiguration { site = this.site?: DefaultConfiguration.site, service = this.service, verbosity = this.verbosity, - nativeCrashReportEnabled = this.nativeCrashReportEnabled ?: DefaultConfiguration.nativeCrashReportEnabled, - nativeLongTaskThresholdMs = this.nativeLongTaskThresholdMs ?: DefaultConfiguration.nativeLongTaskThresholdMs, trackingConsent = this.trackingConsent ?: DefaultConfiguration.trackingConsent, uploadFrequency = this.uploadFrequency ?: DefaultConfiguration.uploadFrequency, batchSize = this.batchSize?: DefaultConfiguration.batchSize, @@ -278,8 +278,6 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap { map.putString("site", site.toString()) service?.let { map.putString("service", it) } verbosity?.let { map.putString("verbosity", it) } - nativeCrashReportEnabled?.let { map.putBoolean("nativeCrashReportEnabled", it) } - nativeLongTaskThresholdMs?.let { map.putDouble("nativeLongTaskThresholdMs", it) } map.putString("trackingConsent", trackingConsent.toString()) map.putString("uploadFrequency", uploadFrequency.toString()) map.putString("batchSize", batchSize.toString()) @@ -322,8 +320,11 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap { rum.trackFrustrations?.let { rumMap.putBoolean("trackFrustrations", it) } rum.longTaskThresholdMs?.let { map.putDouble("longTaskThresholdMs", it) } rum.sessionSampleRate?.let { rumMap.putDouble("sessionSampleRate", it) } + rum.resourceTraceSampleRate?.let { rumMap.putDouble("resourceTraceSampleRate", it) } rum.vitalsUpdateFrequency?.let { rumMap.putString("vitalsUpdateFrequency", it) } rum.trackBackgroundEvents?.let { rumMap.putBoolean("trackBackgroundEvents", it) } + rum.nativeCrashReportEnabled?.let { rumMap.putBoolean("nativeCrashReportEnabled", it) } + rum.nativeLongTaskThresholdMs?.let { rumMap.putDouble("nativeLongTaskThresholdMs", it) } rum.nativeViewTracking?.let { rumMap.putBoolean("nativeViewTracking", it) } rum.nativeInteractionTracking?.let { rumMap.putBoolean("nativeInteractionTracking", it) } rum.trackNonFatalAnrs?.let { rumMap.putBoolean("trackNonFatalAnrs", it) } @@ -342,7 +343,6 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap { } traceConfiguration?.let { trace -> val traceMap = WritableNativeMap() - trace.resourceTraceSampleRate?.let { traceMap.putDouble("resourceTraceSampleRate", it) } trace.customEndpoint?.let { traceMap.putString("customEndpoint", it) } map.putMap("traceConfiguration", traceMap) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index cf0154af7..e65d1bcf7 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -321,13 +321,15 @@ class DdSdkImplementation( return frameRateProvider } + @Suppress("CyclomaticComplexMethod") private fun buildFrameTimeCallback( ddSdkConfiguration: DdSdkConfiguration ): ((Double) -> Unit)? { val jsRefreshRateMonitoringEnabled = - buildVitalUpdateFrequency(ddSdkConfiguration.rumConfiguration?.vitalsUpdateFrequency) != + ddSdkConfiguration.rumConfiguration != null && + buildVitalUpdateFrequency(ddSdkConfiguration.rumConfiguration.vitalsUpdateFrequency) != VitalsUpdateFrequency.NEVER - val jsLongTasksMonitoringEnabled = ddSdkConfiguration.rumConfiguration?.longTaskThresholdMs != 0.0 + val jsLongTasksMonitoringEnabled = ddSdkConfiguration.rumConfiguration != null && ddSdkConfiguration.rumConfiguration.longTaskThresholdMs != 0.0 if (!jsLongTasksMonitoringEnabled && !jsRefreshRateMonitoringEnabled) { return null diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 4e9673268..1d0c737a5 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -46,12 +46,25 @@ class DdSdkNativeInitialization internal constructor( ) { internal fun initialize(ddSdkConfiguration: DdSdkConfiguration) { val sdkConfiguration = buildSdkConfiguration(ddSdkConfiguration) - val rumConfiguration = buildRumConfiguration(ddSdkConfiguration) - val logsConfiguration = buildLogsConfiguration(ddSdkConfiguration) - val traceConfiguration = buildTraceConfiguration(ddSdkConfiguration) val trackingConsent = buildTrackingConsent(ddSdkConfiguration.trackingConsent) + var rumConfiguration: RumConfiguration? = null + var logsConfiguration: LogsConfiguration? = null + var traceConfiguration: TraceConfiguration? = null + + if (ddSdkConfiguration.rumConfiguration != null) { + rumConfiguration = buildRumConfiguration(ddSdkConfiguration) + } + + if (ddSdkConfiguration.logsConfiguration != null) { + logsConfiguration = buildLogsConfiguration(ddSdkConfiguration) + } + + if (ddSdkConfiguration.traceConfiguration != null) { + traceConfiguration = buildTraceConfiguration(ddSdkConfiguration) + } configureSdkVerbosity(ddSdkConfiguration) + configureRumAndTracesForLogs(ddSdkConfiguration) if (datadog.isInitialized()) { @@ -64,9 +77,17 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) - Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) + if (rumConfiguration != null) { + Rum.enable(rumConfiguration, Datadog.getInstance()) + } + + if (logsConfiguration != null) { + Logs.enable(logsConfiguration, Datadog.getInstance()) + } + + if (traceConfiguration != null) { + Trace.enable(traceConfiguration, Datadog.getInstance()) + } } private fun configureRumAndTracesForLogs(configuration: DdSdkConfiguration) { @@ -131,7 +152,7 @@ class DdSdkNativeInitialization internal constructor( val telemetrySampleRate = (configuration.rumConfiguration?.telemetrySampleRate as? Number)?.toFloat() telemetrySampleRate?.let { configBuilder.setTelemetrySampleRate(it) } - val longTask = (configuration.nativeLongTaskThresholdMs as? Number)?.toLong() + val longTask = (configuration.rumConfiguration?.nativeLongTaskThresholdMs as? Number)?.toLong() if (longTask != null) { configBuilder.trackLongTasks(longTask) } @@ -179,14 +200,14 @@ class DdSdkNativeInitialization internal constructor( event: TelemetryConfigurationEvent ): TelemetryConfigurationEvent? { event.telemetry.configuration.trackNativeErrors = - configuration.nativeCrashReportEnabled + configuration.rumConfiguration?.nativeCrashReportEnabled // trackCrossPlatformLongTasks will be deprecated for trackLongTask event.telemetry.configuration.trackCrossPlatformLongTasks = configuration.rumConfiguration?.longTaskThresholdMs != 0.0 event.telemetry.configuration.trackLongTask = configuration.rumConfiguration?.longTaskThresholdMs != 0.0 event.telemetry.configuration.trackNativeLongTasks = - configuration.nativeLongTaskThresholdMs != 0.0 + configuration.rumConfiguration?.nativeLongTaskThresholdMs != 0.0 event.telemetry.configuration.initializationType = configuration.configurationForTelemetry?.initializationType @@ -264,7 +285,7 @@ class DdSdkNativeInitialization internal constructor( } as Map? ?: emptyMap() ) - configBuilder.setCrashReportsEnabled(configuration.nativeCrashReportEnabled ?: false) + configBuilder.setCrashReportsEnabled(configuration.rumConfiguration?.nativeCrashReportEnabled ?: false) configBuilder.useSite(buildSite(configuration.site)) configBuilder.setUploadFrequency( buildUploadFrequency(configuration.uploadFrequency) diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt index b5ccce1e2..c402a7b28 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt @@ -93,8 +93,6 @@ internal class DdSdkNativeInitializationTest { assertThat(configuration.trackingConsent).isEqualTo("NOT_GRANTED") assertThat(configuration.uploadFrequency).isEqualTo("FREQUENT") assertThat(configuration.batchSize).isEqualTo("SMALL") - assertThat(configuration.nativeCrashReportEnabled).isEqualTo(true) - assertThat(configuration.nativeLongTaskThresholdMs).isEqualTo(333.0) assertThat(configuration.verbosity).isEqualTo("WARN") assertThat(configuration.service).isEqualTo("my.app") assertThat(configuration.additionalConfiguration?.get("_dd.source")).isEqualTo( @@ -109,6 +107,8 @@ internal class DdSdkNativeInitializationTest { assertThat(configuration.rumConfiguration?.telemetrySampleRate).isEqualTo(60.0) assertThat(configuration.rumConfiguration?.vitalsUpdateFrequency).isEqualTo("NEVER") assertThat(configuration.rumConfiguration?.trackFrustrations).isEqualTo(false) + assertThat(configuration.rumConfiguration?.nativeCrashReportEnabled).isEqualTo(true) + assertThat(configuration.rumConfiguration?.nativeLongTaskThresholdMs).isEqualTo(333.0) assertThat(configuration.rumConfiguration?.nativeViewTracking).isEqualTo(true) assertThat(configuration.rumConfiguration?.nativeInteractionTracking).isEqualTo(true) assertThat(configuration.rumConfiguration?.trackBackgroundEvents).isEqualTo(true) @@ -156,8 +156,6 @@ internal class DdSdkNativeInitializationTest { assertThat(configuration.service).isNull() assertThat(configuration.proxyConfiguration).isNull() assertThat(configuration.firstPartyHosts).isNull() - assertThat(configuration.nativeCrashReportEnabled).isEqualTo(false) - assertThat(configuration.nativeLongTaskThresholdMs).isEqualTo(200.0) assertThat(configuration.site).isEqualTo("US1") assertThat(configuration.uploadFrequency).isEqualTo("AVERAGE") assertThat(configuration.batchSize).isEqualTo("MEDIUM") @@ -178,6 +176,8 @@ internal class DdSdkNativeInitializationTest { assertThat(configuration.rumConfiguration?.trackFrustrations).isEqualTo(true) assertThat(configuration.rumConfiguration?.trackBackgroundEvents).isEqualTo(false) assertThat(configuration.rumConfiguration?.customEndpoint).isNull() + assertThat(configuration.rumConfiguration?.nativeCrashReportEnabled).isEqualTo(false) + assertThat(configuration.rumConfiguration?.nativeLongTaskThresholdMs).isEqualTo(200.0) assertThat(configuration.rumConfiguration?.nativeViewTracking).isEqualTo(false) assertThat(configuration.rumConfiguration?.nativeInteractionTracking).isEqualTo(false) assertThat(configuration.logsConfiguration?.customEndpoint).isNull() diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 2f32a37e8..664c2a758 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -184,7 +184,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=true}`() { // Given - val bridgeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + val bridgeConfiguration = fakeConfiguration.copy(rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -238,7 +241,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=false}`() { // Given - fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = false + ) + fakeConfiguration = fakeConfiguration.copy(site = null, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -292,7 +298,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=null}`() { // Given - fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = false + ) + fakeConfiguration = fakeConfiguration.copy(site = null, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() @@ -575,7 +584,10 @@ internal class DdSdkTest { forge: Forge ) { // Given - fakeConfiguration = fakeConfiguration.copy(site = null, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = null, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -632,7 +644,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("us1") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -689,7 +704,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("us3") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -746,7 +764,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("us5") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -803,7 +824,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("us1_fed") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -860,7 +884,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("eu1") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -917,7 +944,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("ap1") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -974,7 +1004,10 @@ internal class DdSdkTest { ) { // Given val site = forge.randomizeCase("ap2") - fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = true + ) + fakeConfiguration = fakeConfiguration.copy(site = site, rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -1601,9 +1634,11 @@ internal class DdSdkTest { val threshold = forge.aDouble(min = 100.0, max = 65536.0) // Given - val bridgeConfiguration = configuration.copy( + val rumConfiguration = configuration.rumConfiguration?.copy( nativeLongTaskThresholdMs = threshold ) + val bridgeConfiguration = configuration.copy(rumConfiguration = rumConfiguration) + val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -1656,9 +1691,10 @@ internal class DdSdkTest { forge: Forge ) { // Given - val bridgeConfiguration = configuration.copy( + val rumConfiguration = configuration.rumConfiguration?.copy( nativeLongTaskThresholdMs = 0.0 ) + val bridgeConfiguration = configuration.copy(rumConfiguration = rumConfiguration) val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() @@ -2583,11 +2619,11 @@ internal class DdSdkTest { ) { // Given val rumConfiguration = configuration.rumConfiguration?.copy( - longTaskThresholdMs = 0.0 + longTaskThresholdMs = 0.0, + nativeCrashReportEnabled = trackNativeErrors, + nativeLongTaskThresholdMs = 0.0 ) val bridgeConfiguration = configuration.copy( - nativeCrashReportEnabled = trackNativeErrors, - nativeLongTaskThresholdMs = 0.0, rumConfiguration = rumConfiguration, configurationForTelemetry = ConfigurationForTelemetry( initializationType = initializationType, @@ -3261,7 +3297,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native SDK 𝕎 initialize() {synthethics attributes}`() { // Given - fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) + val rumConfiguration = fakeConfiguration.rumConfiguration?.copy( + nativeCrashReportEnabled = false + ) + fakeConfiguration = fakeConfiguration.copy(site = null, rumConfiguration = rumConfiguration) DdSdkSynthetics.testId = "unit-test-test-id" DdSdkSynthetics.resultId = "unit-test-result-id" diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt index 94dbd95ae..252286947 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt @@ -27,10 +27,6 @@ fun DdSdkConfiguration.toReadableJavaOnlyMap(): ReadableMap { service?.let { map["service"] = it } verbosity?.let { map["verbosity"] = it } - map["nativeCrashReportEnabled"] = nativeCrashReportEnabled ?: false - - map["nativeLongTaskThresholdMs"] = nativeLongTaskThresholdMs ?: 0.0 - trackingConsent?.let { map["trackingConsent"] = it } map["uploadFrequency"] = uploadFrequency ?: UploadFrequency.AVERAGE.toString() @@ -60,9 +56,12 @@ fun DdSdkConfiguration.toReadableJavaOnlyMap(): ReadableMap { rumMap["trackFrustrations"] = rum?.trackFrustrations ?: false rumMap["longTaskThresholdMs"] = rum?.longTaskThresholdMs ?: 0.0 rumMap["sessionSampleRate"] = rum?.sessionSampleRate ?: 100.0 + rum?.resourceTraceSampleRate?.let { rumMap["resourceTraceSampleRate"] = it } rumMap["vitalsUpdateFrequency"] = rum?.vitalsUpdateFrequency ?: VitalsUpdateFrequency.AVERAGE.toString() rumMap["trackBackgroundEvents"] = rum?.trackBackgroundEvents ?: false + rumMap["nativeCrashReportEnabled"] = rum?.nativeCrashReportEnabled ?: false + rumMap["nativeLongTaskThresholdMs"] = rum?.nativeLongTaskThresholdMs ?: 0.0 rum?.nativeViewTracking?.let { rumMap["nativeViewTracking"] = it } rum?.nativeInteractionTracking?.let { rumMap["nativeInteractionTracking"] = it } rum?.trackNonFatalAnrs?.let { rumMap["trackNonFatalAnrs"] = it } @@ -83,7 +82,6 @@ fun DdSdkConfiguration.toReadableJavaOnlyMap(): ReadableMap { traceConfiguration?.let { trace -> val traceMap = mutableMapOf() - trace.resourceTraceSampleRate?.let { traceMap["resourceTraceSampleRate"] = it } trace.customEndpoint?.let { traceMap["customEndpoint"] = it } map["traceConfiguration"] = traceMap.toReadableMap() diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt index cc903fa0a..3f846b988 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt @@ -31,8 +31,6 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory { site = forge.aNullable { anElementFrom("US", "EU", "GOV") }, service = forge.aNullable { forge.anAlphabeticalString() }, verbosity = forge.aNullable { anElementFrom("debug", "info", "warn", "error") }, - nativeCrashReportEnabled = forge.aNullable { aBool() }, - nativeLongTaskThresholdMs = forge.aNullable { aDouble(100.0, 5000.0) }, trackingConsent = forge.aNullable { anElementFrom("pending", "granted", "not_granted") }, @@ -64,10 +62,13 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory { trackFrustrations = forge.aNullable { aBool() }, longTaskThresholdMs = forge.aNullable { aDouble(0.0, 100.0) }, sessionSampleRate = forge.aNullable { aDouble(0.0, 100.0) }, + resourceTraceSampleRate = forge.aNullable { aDouble(0.0, 100.0) }, vitalsUpdateFrequency = forge.aNullable { anElementFrom("RARE", "NEVER", "FREQUENT", "AVERAGE") }, trackBackgroundEvents = forge.aNullable { forge.aBool() }, + nativeCrashReportEnabled = forge.aNullable { aBool() }, + nativeLongTaskThresholdMs = forge.aNullable { aDouble(100.0, 5000.0) }, nativeViewTracking = forge.aNullable { aBool() }, nativeInteractionTracking = forge.aNullable { aBool() }, trackNonFatalAnrs = forge.aNullable { aBool() }, @@ -81,7 +82,6 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory { customEndpoint = forge.aNullable { aString() } ), traceConfiguration = TraceConfiguration( - resourceTraceSampleRate = forge.aNullable { aDouble(0.0, 100.0) }, customEndpoint = forge.aNullable { aString() } ), configurationForTelemetry = ConfigurationForTelemetry( diff --git a/packages/core/android/src/test/resources/input/complete-configuration.json b/packages/core/android/src/test/resources/input/complete-configuration.json index 8c61eda5b..b6079988c 100644 --- a/packages/core/android/src/test/resources/input/complete-configuration.json +++ b/packages/core/android/src/test/resources/input/complete-configuration.json @@ -8,18 +8,18 @@ "verbosity": "WARN", "site": "US3", "version": "2.3.1", - "useAccessibilityLabel": true, "batchSize": "SMALL", - "nativeCrashReportEnabled": true, - "nativeLongTaskThresholdMs": 333, "uploadFrequency": "FREQUENT", "rumConfiguration": { "applicationId": "fake-app-id", + "useAccessibilityLabel": true, "trackInteractions": true, "trackResources": true, "trackErrors": true, "nativeViewTracking": true, "longTaskThresholdMs": 44, + "nativeCrashReportEnabled": true, + "nativeLongTaskThresholdMs": 333, "nativeInteractionTracking": true, "actionNameAttribute": "action-name-attr", "customEndpoint": "https://rum.example.com", @@ -27,6 +27,7 @@ "initialResourceThreshold": 0.5, "telemetrySampleRate": 60, "sessionSampleRate": 80, + "resourceTraceSampleRate": 33, "trackBackgroundEvents": true, "trackFrustrations": false }, @@ -34,7 +35,6 @@ "customEndpoint": "https://logs.example.com" }, "traceConfiguration": { - "resourceTraceSampleRate": 33, "customEndpoint": "https://trace.example.com" }, "firstPartyHosts": [ diff --git a/packages/core/datadog-configuration.schema.json b/packages/core/datadog-configuration.schema.json index f85b9ae3d..be0a2d84e 100644 --- a/packages/core/datadog-configuration.schema.json +++ b/packages/core/datadog-configuration.schema.json @@ -26,10 +26,6 @@ "NOT_GRANTED" ] }, - "useAccessibilityLabel": { - "description": "Determines whether the accessibility labels can be used to name RUM actions. Defaults to `true`.", - "type": "boolean" - }, "additionalConfiguration": { "description": "Additional custom configuration passed as key/value pairs. This is forwarded to the native SDKs.", "type": "object", @@ -53,17 +49,6 @@ "HIGH" ] }, - "nativeCrashReportEnabled": { - "description": "Enables crash reporting for native platforms (iOS, Android). Default `false`.", - "type": "boolean" - }, - "nativeLongTaskThresholdMs": { - "description": "The threshold for native long tasks reporting in milliseconds. Set to a number to enable reporting or `false` to disable.", - "type": [ - "integer", - "boolean" - ] - }, "proxyConfiguration": { "type": "object", "description": "Configuration for proxying SDK data.", @@ -179,6 +164,10 @@ "description": "The RUM application ID.", "type": "string" }, + "useAccessibilityLabel": { + "description": "Determines whether the accessibility labels can be used to name RUM actions. Defaults to `true`.", + "type": "boolean" + }, "trackInteractions": { "description": "Track React Native components interactions.", "type": "boolean" @@ -218,10 +207,24 @@ "description": "Enables native interaction tracking.", "type": "boolean" }, + "nativeCrashReportEnabled": { + "description": "Enables crash reporting for native platforms (iOS, Android). Default `false`.", + "type": "boolean" + }, + "nativeLongTaskThresholdMs": { + "description": "The threshold for native long tasks reporting in milliseconds. Set to a number to enable reporting or `false` to disable.", + "type": [ + "integer" + ] + }, "sessionSampleRate": { "description": "Percentage of sampled RUM sessions. Range `0`-`100`. (default `100`).", "type": "integer" }, + "resourceTraceSampleRate": { + "description": "Percentage of tracing integrations for network calls between your app and your backend. Range `0`-`100` (default `100`).", + "type": "number" + }, "trackBackgroundEvents": { "description": "Enables tracking of RUM events when no RUM View is active.", "type": "boolean" @@ -287,10 +290,6 @@ "description": "Traces-specific configuration options.", "type": "object", "properties": { - "resourceTraceSampleRate": { - "description": "Percentage of tracing integrations for network calls between your app and your backend. Range `0`-`100` (default `100`).", - "type": "number" - }, "customEndpoint": { "description": "Target a custom server for Traces", "type": "string" diff --git a/packages/core/ios/Sources/DdSdkConfiguration.swift b/packages/core/ios/Sources/DdSdkConfiguration.swift index 9bd482bd9..25193da9e 100644 --- a/packages/core/ios/Sources/DdSdkConfiguration.swift +++ b/packages/core/ios/Sources/DdSdkConfiguration.swift @@ -18,9 +18,6 @@ import Foundation /// - site: The Datadog site of your organization (e.g., `US1`, `US1_FED`, `US3`, `US5`, `EU1`). /// - service: The custom service name reported for logs, traces, and RUM. /// - verbosity: Verbosity level of the SDK’s internal logging (`DEBUG`, `INFO`, `WARN`, `ERROR`). -/// - nativeCrashReportEnabled: Whether the SDK should track native (iOS / Android) crashes. -/// Default is `false`. -/// - nativeLongTaskThresholdMs: The threshold for reporting native long tasks in milliseconds. /// - trackingConsent: User tracking consent (`pending`, `granted`, `not_granted`). /// - uploadFrequency: The frequency at which batches of data are uploaded. /// - batchSize: The preferred size of batches sent to Datadog. @@ -39,8 +36,6 @@ public class DdSdkConfiguration: NSObject { public var site: DatadogSite public var service: NSString? = nil public var verbosity: NSString? = nil - public var nativeCrashReportEnabled: Bool? = nil - public var nativeLongTaskThresholdMs: Double? = nil public var trackingConsent: TrackingConsent public var uploadFrequency: Datadog.Configuration.UploadFrequency public var batchSize: Datadog.Configuration.BatchSize @@ -59,8 +54,6 @@ public class DdSdkConfiguration: NSObject { site: DatadogSite, service: NSString?, verbosity: NSString? = nil, - nativeCrashReportEnabled: Bool? = nil, - nativeLongTaskThresholdMs: Double? = nil, trackingConsent: TrackingConsent, uploadFrequency: Datadog.Configuration.UploadFrequency, batchSize: Datadog.Configuration.BatchSize, @@ -78,8 +71,6 @@ public class DdSdkConfiguration: NSObject { self.site = site self.service = service self.verbosity = verbosity - self.nativeCrashReportEnabled = nativeCrashReportEnabled - self.nativeLongTaskThresholdMs = nativeLongTaskThresholdMs self.trackingConsent = trackingConsent self.uploadFrequency = uploadFrequency self.batchSize = batchSize @@ -103,6 +94,9 @@ public class DdSdkConfiguration: NSObject { /// - vitalsUpdateFrequency: Frequency at which the SDK collects mobile vitals metrics. /// - trackBackgroundEvents: Enables/disables tracking RUM events when no RUM View is active. /// May increase the number of sessions and billing. +/// - nativeCrashReportEnabled: Whether the SDK should track native (iOS / Android) crashes. +/// Default is `false`. +/// - nativeLongTaskThresholdMs: The threshold for reporting native long tasks in milliseconds. /// - nativeViewTracking: Enables tracking of native iOS/Android UI views. /// - nativeInteractionTracking: Enables tracking of native UI interactions. /// - appHangThreshold: Threshold in seconds for reporting non-fatal app hangs (iOS only). @@ -117,8 +111,11 @@ public class RumConfiguration: NSObject { public var trackFrustrations: Bool? = true public var longTaskThresholdMs: Double = 0.0 public var sessionSampleRate: Double? = nil + public var resourceTraceSampleRate: Double? = nil public var vitalsUpdateFrequency: RUM.Configuration.VitalsFrequency? = nil public var trackBackgroundEvents: Bool? = nil + public var nativeCrashReportEnabled: Bool? = nil + public var nativeLongTaskThresholdMs: Double? = nil public var nativeViewTracking: Bool? = nil public var nativeInteractionTracking: Bool? = nil public var appHangThreshold: Double? = nil @@ -133,8 +130,11 @@ public class RumConfiguration: NSObject { trackFrustrations: Bool?, longTaskThresholdMs: Double, sessionSampleRate: Double?, + resourceTraceSampleRate: Double?, vitalsUpdateFrequency: RUM.Configuration.VitalsFrequency?, trackBackgroundEvents: Bool?, + nativeCrashReportEnabled: Bool? = nil, + nativeLongTaskThresholdMs: Double? = nil, nativeViewTracking: Bool?, nativeInteractionTracking: Bool?, appHangThreshold: Double?, @@ -148,8 +148,11 @@ public class RumConfiguration: NSObject { self.trackFrustrations = trackFrustrations self.longTaskThresholdMs = longTaskThresholdMs self.sessionSampleRate = sessionSampleRate + self.resourceTraceSampleRate = resourceTraceSampleRate self.vitalsUpdateFrequency = vitalsUpdateFrequency self.trackBackgroundEvents = trackBackgroundEvents + self.nativeCrashReportEnabled = nativeCrashReportEnabled + self.nativeLongTaskThresholdMs = nativeLongTaskThresholdMs self.nativeViewTracking = nativeViewTracking self.nativeInteractionTracking = nativeInteractionTracking self.appHangThreshold = appHangThreshold @@ -190,14 +193,11 @@ public class LogsConfiguration: NSObject { /// - resourceTracingSamplingRate: Percentage (0–100) of network resource traces to sample. /// - customEndpoint: A custom Trace intake endpoint to override the default Datadog intake. public class TraceConfiguration: NSObject { - public var resourceTraceSampleRate: Double? = nil public var customEndpoint: String? = nil init( - resourceTraceSampleRate: Double?, customEndpoint: String? ) { - self.resourceTraceSampleRate = resourceTraceSampleRate self.customEndpoint = customEndpoint } } diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 71c1bb7b7..8a2a455d8 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -15,7 +15,7 @@ import Foundation import React #if os(iOS) -import DatadogWebViewTracking + import DatadogWebViewTracking #endif func getDefaultAppVersion() -> String { @@ -30,12 +30,12 @@ public class DdSdkImplementation: NSObject { let jsDispatchQueue: DispatchQueueType let jsRefreshRateMonitor: RefreshRateMonitor let mainDispatchQueue: DispatchQueueType - let RUMMonitorProvider: () -> RUMMonitorProtocol - let RUMMonitorInternalProvider: () -> RUMMonitorInternalProtocol? + var RUMMonitorProvider: () -> RUMMonitorProtocol? + var RUMMonitorInternalProvider: () -> RUMMonitorInternalProtocol? -#if os(iOS) - var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? -#endif + #if os(iOS) + var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? + #endif private let jsLongTaskThresholdInSeconds: TimeInterval = 0.1 @@ -44,9 +44,7 @@ public class DdSdkImplementation: NSObject { self.init( mainDispatchQueue: DispatchQueue.main, jsDispatchQueue: bridge, - jsRefreshRateMonitor: JSRefreshRateMonitor.init(), - RUMMonitorProvider: { RUMMonitor.shared() }, - RUMMonitorInternalProvider: { RUMMonitor.shared()._internal } + jsRefreshRateMonitor: JSRefreshRateMonitor.init() ) } @@ -54,14 +52,14 @@ public class DdSdkImplementation: NSObject { mainDispatchQueue: DispatchQueueType, jsDispatchQueue: DispatchQueueType, jsRefreshRateMonitor: RefreshRateMonitor, - RUMMonitorProvider: @escaping () -> RUMMonitorProtocol, - RUMMonitorInternalProvider: @escaping () -> RUMMonitorInternalProtocol? + RUMMonitorProvider: (() -> RUMMonitorProtocol)? = nil, + RUMMonitorInternalProvider: (() -> RUMMonitorInternalProtocol?)? = nil ) { self.mainDispatchQueue = mainDispatchQueue self.jsDispatchQueue = jsDispatchQueue self.jsRefreshRateMonitor = jsRefreshRateMonitor - self.RUMMonitorProvider = RUMMonitorProvider - self.RUMMonitorInternalProvider = RUMMonitorInternalProvider + self.RUMMonitorProvider = RUMMonitorProvider ?? { nil } + self.RUMMonitorInternalProvider = RUMMonitorInternalProvider ?? { nil } super.init() } @@ -75,49 +73,72 @@ public class DdSdkImplementation: NSObject { let nativeInitialization = DdSdkNativeInitialization() nativeInitialization.initialize(sdkConfiguration: sdkConfiguration) + + // TO DO - We should have a better way to determine if rum is enabled + // core?.get(feature: RUMfeature.self ) would be nice but RUMfeature is an internal property + if sdkConfiguration.rumConfiguration != nil { + + // Only overwrite monitors if they weren't set already (as they are sometimes during unit test runs + if self.RUMMonitorProvider() == nil { + self.RUMMonitorProvider = { RUMMonitor.shared() } + } + if self.RUMMonitorInternalProvider() == nil { + self.RUMMonitorInternalProvider = { RUMMonitor.shared()._internal } + } + } + self.startJSRefreshRateMonitoring(sdkConfiguration: sdkConfiguration) self.overrideReactNativeTelemetry(rnConfiguration: sdkConfiguration) resolve(nil) } - + @objc - public func addAttribute(key: AttributeKey, value: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func addAttribute( + key: AttributeKey, value: NSDictionary, resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { if let attributeValue = value.object(forKey: "value") { let castedValue = castValueToSwift(attributeValue) - RUMMonitorProvider().addAttribute(forKey: key, value: castedValue) + RUMMonitorProvider()?.addAttribute(forKey: key, value: castedValue) GlobalState.addAttribute(forKey: key, value: castedValue) } - + resolve(nil) } - + @objc - public func removeAttribute(key: AttributeKey, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - RUMMonitorProvider().removeAttribute(forKey: key) + public func removeAttribute( + key: AttributeKey, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + RUMMonitorProvider()?.removeAttribute(forKey: key) GlobalState.removeAttribute(key: key) - + resolve(nil) } @objc - public func addAttributes(attributes: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func addAttributes( + attributes: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { let castedAttributes = castAttributesToSwift(attributes) for (key, value) in castedAttributes { - RUMMonitorProvider().addAttribute(forKey: key, value: value) + RUMMonitorProvider()?.addAttribute(forKey: key, value: value) GlobalState.addAttribute(forKey: key, value: value) } resolve(nil) } - + @objc - public func removeAttributes(keys: [AttributeKey], resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - RUMMonitorProvider().removeAttributes(forKeys: keys) + public func removeAttributes( + keys: [AttributeKey], resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + RUMMonitorProvider()?.removeAttributes(forKeys: keys) for (key) in keys { GlobalState.removeAttribute(key: key) } - + resolve(nil) } @@ -220,37 +241,45 @@ public class DdSdkImplementation: NSObject { @objc - public func telemetryDebug(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DdTelemetry.telemetryDebug(id: "datadog_react_native:\(message)", message: message as String) + public func telemetryDebug( + message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + DdTelemetry.telemetryDebug( + id: "datadog_react_native:\(message)", message: message as String) resolve(nil) } @objc - public func telemetryError(message: NSString, stack: NSString, kind: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DdTelemetry.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) + public func telemetryError( + message: NSString, stack: NSString, kind: NSString, resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + DdTelemetry.telemetryError( + id: "datadog_react_native:\(String(describing: kind)):\(message)", + message: message as String, kind: kind as String, stack: stack as String) resolve(nil) } -#if os(iOS) - @objc - public func consumeWebviewEvent( - message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock - ) { - do { - try DatadogSDKWrapper.shared.sendWebviewMessage(body: message) - } catch { - DdTelemetry.telemetryError( - id: "datadog_react_native:\(error.localizedDescription)", - message: "The message being sent was:\(message)" as String, - kind: "WebViewEventBridgeError" as String, - stack: String(describing: error) as String) + #if os(iOS) + @objc + public func consumeWebviewEvent( + message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + do { + try DatadogSDKWrapper.shared.sendWebviewMessage(body: message) + } catch { + DdTelemetry.telemetryError( + id: "datadog_react_native:\(error.localizedDescription)", + message: "The message being sent was:\(message)" as String, + kind: "WebViewEventBridgeError" as String, + stack: String(describing: error) as String) + } + + resolve(nil) } - resolve(nil) - } + #endif -#endif - @objc public func clearAllData(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { Datadog.clearAllData() @@ -268,8 +297,8 @@ public class DdSdkImplementation: NSObject { trackErrors: rnConfiguration.configurationForTelemetry?.trackErrors, trackInteractions: rnConfiguration.configurationForTelemetry?.trackInteractions, trackLongTask: rnConfiguration.rumConfiguration?.longTaskThresholdMs != 0, - trackNativeErrors: rnConfiguration.nativeLongTaskThresholdMs != 0, - trackNativeLongTasks: rnConfiguration.nativeLongTaskThresholdMs != 0, + trackNativeErrors: rnConfiguration.rumConfiguration?.nativeLongTaskThresholdMs != 0, + trackNativeLongTasks: rnConfiguration.rumConfiguration?.nativeLongTaskThresholdMs != 0, trackNetworkRequests: rnConfiguration.configurationForTelemetry?.trackNetworkRequests ) } @@ -283,8 +312,12 @@ public class DdSdkImplementation: NSObject { } func buildFrameTimeCallback(sdkConfiguration: DdSdkConfiguration) -> ((Double) -> Void)? { - let jsRefreshRateMonitoringEnabled = sdkConfiguration.rumConfiguration?.vitalsUpdateFrequency != nil - let jsLongTaskMonitoringEnabled = sdkConfiguration.rumConfiguration?.longTaskThresholdMs != 0 + let jsRefreshRateMonitoringEnabled = + sdkConfiguration.rumConfiguration != nil + && sdkConfiguration.rumConfiguration?.vitalsUpdateFrequency != nil + let jsLongTaskMonitoringEnabled = + sdkConfiguration.rumConfiguration != nil + && sdkConfiguration.rumConfiguration?.longTaskThresholdMs != 0 if !jsRefreshRateMonitoringEnabled && !jsLongTaskMonitoringEnabled { return nil @@ -295,7 +328,8 @@ public class DdSdkImplementation: NSObject { let shouldRecordFrameTime = jsRefreshRateMonitoringEnabled && frameTime > 0 let shouldRecordLongTask = jsLongTaskMonitoringEnabled - && frameTime > (sdkConfiguration.rumConfiguration?.longTaskThresholdMs ?? 0.0) / 1_000 + && frameTime > (sdkConfiguration.rumConfiguration?.longTaskThresholdMs ?? 0.0) + / 1_000 guard shouldRecordFrameTime || shouldRecordLongTask, let rumMonitorInternal = RUMMonitorInternalProvider() else { return } @@ -304,9 +338,12 @@ public class DdSdkImplementation: NSObject { let now = Date() // Leave JS thread ASAP to give as much time to JS engine work. sharedQueue.async { - if (shouldRecordFrameTime) { - let normalizedFrameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(frameTime) - rumMonitorInternal.updatePerformanceMetric(at: now, metric: .jsFrameTimeSeconds, value: normalizedFrameTimeSeconds, attributes: [:]) + if shouldRecordFrameTime { + let normalizedFrameTimeSeconds = + DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(frameTime) + rumMonitorInternal.updatePerformanceMetric( + at: now, metric: .jsFrameTimeSeconds, value: normalizedFrameTimeSeconds, + attributes: [:]) } if shouldRecordLongTask { rumMonitorInternal.addLongTask( @@ -318,23 +355,28 @@ public class DdSdkImplementation: NSObject { return frameTimeCallback } - + // Normalizes frameTime values so when they are turned into FPS metrics they are normalized on a range between 0 and fpsBudget. If fpsBudget is not provided it will default to 60hz. - public static func normalizeFrameTimeForDeviceRefreshRate(_ frameTime: Double, fpsBudget: Double? = nil, deviceDisplayFps: Double? = nil) -> Double { + public static func normalizeFrameTimeForDeviceRefreshRate( + _ frameTime: Double, fpsBudget: Double? = nil, deviceDisplayFps: Double? = nil + ) -> Double { let DEFAULT_REFRESH_HZ = 60.0 let frameTimeMs: Double = frameTime * 1000.0 let frameBudgetHz: Double = fpsBudget ?? DEFAULT_REFRESH_HZ let maxDeviceDisplayHz = deviceDisplayFps ?? Double(UIScreen.main.maximumFramesPerSecond) let maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz let budgetFrameTimeMs = 1000.0 / frameBudgetHz - - guard maxDeviceDisplayHz > 0, frameTimeMs.isFinite, frameTimeMs > 0, frameBudgetHz > 0, budgetFrameTimeMs.isFinite, budgetFrameTimeMs > 0, maxDeviceFrameTimeMs.isFinite, maxDeviceFrameTimeMs > 0 else { + + guard maxDeviceDisplayHz > 0, frameTimeMs.isFinite, frameTimeMs > 0, frameBudgetHz > 0, + budgetFrameTimeMs.isFinite, budgetFrameTimeMs > 0, maxDeviceFrameTimeMs.isFinite, + maxDeviceFrameTimeMs > 0 + else { return 1.0 / DEFAULT_REFRESH_HZ } - + var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) - return normalizedFrameTimeMs / 1000.0 // in seconds + return normalizedFrameTimeMs / 1000.0 // in seconds } } diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index db4d8ef48..46ce0dcdf 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -40,7 +40,7 @@ public class DdSdkNativeInitialization: NSObject { id: "datadog_react_native: RN SDK was already initialized in native", message: "RN SDK was already initialized in native" ) - + RUMMonitor.shared().currentSessionID { sessionId in guard let id = sessionId else { return } DdSdkSessionStartedListener.instance.rumSessionListener?(id, false) @@ -85,17 +85,24 @@ public class DdSdkNativeInitialization: NSObject { } func enableFeatures(sdkConfiguration: DdSdkConfiguration) { - let rumConfig = buildRumConfiguration(configuration: sdkConfiguration) - RUM.enable(with: rumConfig) - - let logsConfig = buildLogsConfiguration(configuration: sdkConfiguration) - Logs.enable(with: logsConfig) + + if (sdkConfiguration.rumConfiguration != nil) { + let rumConfig = buildRumConfiguration(configuration: sdkConfiguration) + RUM.enable(with: rumConfig) + + if sdkConfiguration.rumConfiguration?.nativeCrashReportEnabled ?? false { + CrashReporting.enable() + } + } - let traceConfig = buildTraceConfiguration(configuration: sdkConfiguration) - Trace.enable(with: traceConfig) + if (sdkConfiguration.logsConfiguration != nil) { + let logsConfig = buildLogsConfiguration(configuration: sdkConfiguration) + Logs.enable(with: logsConfig) + } - if sdkConfiguration.nativeCrashReportEnabled ?? false { - CrashReporting.enable() + if (sdkConfiguration.traceConfiguration != nil) { + let traceConfig = buildTraceConfiguration(configuration: sdkConfiguration) + Trace.enable(with: traceConfig) } #if os(iOS) @@ -140,7 +147,7 @@ public class DdSdkNativeInitialization: NSObject { } var longTaskThreshold: TimeInterval? = nil - if let threshold = configuration.nativeLongTaskThresholdMs, threshold != 0 { + if let threshold = configuration.rumConfiguration?.nativeLongTaskThresholdMs, threshold != 0 { longTaskThreshold = threshold / 1_000 } @@ -160,7 +167,7 @@ public class DdSdkNativeInitialization: NSObject { firstPartyHostsTracing: .traceWithHeaders( hostsWithHeaders: firstPartyHosts, sampleRate: Float( - configuration.traceConfiguration?.resourceTraceSampleRate + configuration.rumConfiguration?.resourceTraceSampleRate ?? DefaultConfiguration.resourceTraceSampleRate) ) ) @@ -220,8 +227,12 @@ public class DdSdkNativeInitialization: NSObject { } func buildLogsConfiguration(configuration: DdSdkConfiguration) -> Logs.Configuration { + guard let logsConfig = configuration.logsConfiguration else { + preconditionFailure("buildLogsConfiguration called without logsConfiguration") + } + var customLogsEndpointURL: URL? = nil - if let customLogsEndpoint = configuration.logsConfiguration?.customEndpoint as? NSString { + if let customLogsEndpoint = logsConfig.customEndpoint as? NSString { if customLogsEndpoint != "" { customLogsEndpointURL = URL(string: "\(customLogsEndpoint)/api/v2/logs" as String) } @@ -231,8 +242,12 @@ public class DdSdkNativeInitialization: NSObject { } func buildTraceConfiguration(configuration: DdSdkConfiguration) -> Trace.Configuration { + guard let traceConfig = configuration.traceConfiguration else { + preconditionFailure("buildTraceConfiguration called without traceConfiguration") + } + var customTraceEndpointURL: URL? = nil - if let customTraceEndpoint = configuration.traceConfiguration?.customEndpoint as? NSString { + if let customTraceEndpoint = traceConfig.customEndpoint as? NSString { if customTraceEndpoint != "" { customTraceEndpointURL = URL( string: "\(customTraceEndpoint)/api/v2/spans" as String) diff --git a/packages/core/ios/Sources/RNDdSdkConfiguration.swift b/packages/core/ios/Sources/RNDdSdkConfiguration.swift index 46ae57fb6..ad199f922 100644 --- a/packages/core/ios/Sources/RNDdSdkConfiguration.swift +++ b/packages/core/ios/Sources/RNDdSdkConfiguration.swift @@ -23,9 +23,6 @@ extension NSDictionary { let service = self["service"] as? NSString let verbosity = self["verbosity"] as? NSString - let nativeCrashReportEnabled = self["nativeCrashReportEnabled"] as? Bool - let nativeLongTaskThresholdMs = self["nativeLongTaskThresholdMs"] as? Double - let trackingConsentString = self["trackingConsent"] as? NSString let trackingConsent = trackingConsentString.asTrackingConsent() @@ -55,10 +52,13 @@ extension NSDictionary { let trackFrustrations = rumDict["trackFrustrations"] as? Bool let longTaskThresholdMs = rumDict["longTaskThresholdMs"] as? Double let sessionSampleRate = rumDict["sessionSampleRate"] as? Double + let resourceTraceSampleRate = rumDict["resourceTraceSampleRate"] as? Double let vitalsUpdateFrequencyString = rumDict["vitalsUpdateFrequency"] as? NSString let vitalsUpdateFrequency = vitalsUpdateFrequencyString.asVitalsUpdateFrequency() let trackBackgroundEvents = rumDict["trackBackgroundEvents"] as? Bool + let nativeCrashReportEnabled = rumDict["nativeCrashReportEnabled"] as? Bool + let nativeLongTaskThresholdMs = rumDict["nativeLongTaskThresholdMs"] as? Double let nativeViewTracking = rumDict["nativeViewTracking"] as? Bool let nativeInteractionTracking = rumDict["nativeInteractionTracking"] as? Bool @@ -75,9 +75,15 @@ extension NSDictionary { longTaskThresholdMs: longTaskThresholdMs ?? DefaultConfiguration.longTaskThresholdMs, sessionSampleRate: sessionSampleRate ?? DefaultConfiguration.sessionSamplingRate, + resourceTraceSampleRate: resourceTraceSampleRate + ?? DefaultConfiguration.resourceTraceSampleRate, vitalsUpdateFrequency: vitalsUpdateFrequency, trackBackgroundEvents: trackBackgroundEvents ?? DefaultConfiguration.trackBackgroundEvents, + nativeCrashReportEnabled: nativeCrashReportEnabled + ?? DefaultConfiguration.nativeCrashReportEnabled, + nativeLongTaskThresholdMs: nativeLongTaskThresholdMs + ?? DefaultConfiguration.nativeLongTaskThresholdMs, nativeViewTracking: nativeViewTracking ?? DefaultConfiguration.nativeViewTracking, nativeInteractionTracking: nativeInteractionTracking ?? DefaultConfiguration.nativeInteractionTracking, @@ -121,12 +127,9 @@ extension NSDictionary { let traceConfiguration: TraceConfiguration? if let traceDict = traceConfigurationDict { - let resourceTraceSampleRate = traceDict["resourceTraceSampleRate"] as? Double let customEndpoint = traceDict["customEndpoint"] as? String traceConfiguration = TraceConfiguration( - resourceTraceSampleRate: resourceTraceSampleRate - ?? DefaultConfiguration.resourceTraceSampleRate, customEndpoint: customEndpoint ) } else { @@ -145,10 +148,6 @@ extension NSDictionary { site: site, service: service, verbosity: verbosity, - nativeCrashReportEnabled: nativeCrashReportEnabled - ?? DefaultConfiguration.nativeCrashReportEnabled, - nativeLongTaskThresholdMs: nativeLongTaskThresholdMs - ?? DefaultConfiguration.nativeLongTaskThresholdMs, trackingConsent: trackingConsent, uploadFrequency: uploadFrequency, batchSize: batchSize, @@ -306,16 +305,6 @@ extension Dictionary where Key == String, Value == AnyObject { let service = configuration["service"] as? NSString let verbosity = configuration["verbosity"] as? NSString - let nativeCrashReportEnabled = configuration["nativeCrashReportEnabled"] as? Bool - - let nativeLongTaskRaw = configuration["nativeLongTaskThresholdMs"] - let nativeLongTaskThresholdMs: Double? = { - if let v = nativeLongTaskRaw as? Double { return v } - if let v = nativeLongTaskRaw as? Int { return Double(v) } - if let v = nativeLongTaskRaw as? Bool, v == false { return 0.0 } - return nil - }() - let trackingConsentString = configuration["trackingConsent"] as? NSString let trackingConsent = trackingConsentString.asTrackingConsent() @@ -358,6 +347,14 @@ extension Dictionary where Key == String, Value == AnyObject { let vitalsUpdateFrequency = (rum["vitalsUpdateFrequency"] as? NSString).asVitalsUpdateFrequency() + let nativeCrashReportEnabled = rum["nativeCrashReportEnabled"] as? Bool + let nativeLongTaskRaw = rum["nativeLongTaskThresholdMs"] + let nativeLongTaskThresholdMs: Double? = { + if let v = nativeLongTaskRaw as? Double { return v } + if let v = nativeLongTaskRaw as? Int { return Double(v) } + return nil + }() + rumConfiguration = RumConfiguration( applicationId: applicationId, trackFrustrations: rum["trackFrustrations"] as? Bool @@ -365,9 +362,16 @@ extension Dictionary where Key == String, Value == AnyObject { longTaskThresholdMs: longTaskThresholdMs ?? DefaultConfiguration.longTaskThresholdMs, sessionSampleRate: sessionSampleRate ?? DefaultConfiguration.sessionSamplingRate, + resourceTraceSampleRate: + rum["resourceTraceSampleRate"] as? Double + ?? DefaultConfiguration.resourceTraceSampleRate, vitalsUpdateFrequency: vitalsUpdateFrequency, trackBackgroundEvents: rum["trackBackgroundEvents"] as? Bool ?? DefaultConfiguration.trackBackgroundEvents, + nativeCrashReportEnabled: nativeCrashReportEnabled + ?? DefaultConfiguration.nativeCrashReportEnabled, + nativeLongTaskThresholdMs: nativeLongTaskThresholdMs + ?? DefaultConfiguration.nativeLongTaskThresholdMs, nativeViewTracking: rum["nativeViewTracking"] as? Bool ?? DefaultConfiguration.nativeViewTracking, nativeInteractionTracking: rum["nativeInteractionTracking"] as? Bool @@ -410,9 +414,6 @@ extension Dictionary where Key == String, Value == AnyObject { if let trace = traceDict { traceConfiguration = TraceConfiguration( - resourceTraceSampleRate: - trace["resourceTraceSampleRate"] as? Double - ?? DefaultConfiguration.resourceTraceSampleRate, customEndpoint: trace["customEndpoint"] as? String ) } else { @@ -431,10 +432,6 @@ extension Dictionary where Key == String, Value == AnyObject { site: site, service: service, verbosity: verbosity, - nativeCrashReportEnabled: nativeCrashReportEnabled - ?? DefaultConfiguration.nativeCrashReportEnabled, - nativeLongTaskThresholdMs: nativeLongTaskThresholdMs - ?? DefaultConfiguration.nativeLongTaskThresholdMs, trackingConsent: trackingConsent, uploadFrequency: uploadFrequency, batchSize: batchSize, diff --git a/packages/core/ios/Tests/DdSdkNativeInitializationTests.swift b/packages/core/ios/Tests/DdSdkNativeInitializationTests.swift index 247da9086..8babac7a3 100644 --- a/packages/core/ios/Tests/DdSdkNativeInitializationTests.swift +++ b/packages/core/ios/Tests/DdSdkNativeInitializationTests.swift @@ -29,8 +29,6 @@ class DdSdkNativeInitializationTests: XCTestCase { XCTAssertEqual(configuration?.clientToken, "fake-client-token") XCTAssertEqual(configuration?.env, "fake-env") XCTAssertEqual(configuration?.rumConfiguration?.applicationId, "fake-app-id") - XCTAssertEqual(configuration?.nativeCrashReportEnabled, true) - XCTAssertEqual(configuration?.nativeLongTaskThresholdMs, 333.0) XCTAssertEqual(configuration?.rumConfiguration?.longTaskThresholdMs, 44.0) XCTAssertEqual(configuration?.rumConfiguration?.sessionSampleRate, 80.0) XCTAssertEqual(configuration?.site, .us3) @@ -47,6 +45,8 @@ class DdSdkNativeInitializationTests: XCTestCase { XCTAssertEqual(configuration?.additionalConfiguration?["_dd.source"] as! String, "react-native") XCTAssertEqual(configuration?.additionalConfiguration?["_dd.sdk_version"] as! String, SdkVersion) XCTAssertEqual(configuration?.configurationForTelemetry, nil) + XCTAssertEqual(configuration?.rumConfiguration?.nativeCrashReportEnabled, true) + XCTAssertEqual(configuration?.rumConfiguration?.nativeLongTaskThresholdMs, 333.0) XCTAssertEqual(configuration?.rumConfiguration?.nativeViewTracking, true) XCTAssertEqual(configuration?.rumConfiguration?.nativeInteractionTracking, true) XCTAssertEqual(configuration?.verbosity, "WARN") @@ -71,8 +71,6 @@ class DdSdkNativeInitializationTests: XCTestCase { XCTAssertEqual(configuration?.clientToken, "fake-client-token") XCTAssertEqual(configuration?.env, "fake-env") XCTAssertEqual(configuration?.rumConfiguration?.applicationId, "fake-app-id") - XCTAssertEqual(configuration?.nativeCrashReportEnabled, false) - XCTAssertEqual(configuration?.nativeLongTaskThresholdMs, 200.0) XCTAssertEqual(configuration?.rumConfiguration?.longTaskThresholdMs, 0.0) XCTAssertEqual(configuration?.rumConfiguration?.sessionSampleRate, 100.0) XCTAssertEqual(configuration?.site, .us1) @@ -89,6 +87,8 @@ class DdSdkNativeInitializationTests: XCTestCase { XCTAssertEqual(configuration?.additionalConfiguration?["_dd.source"] as! String, "react-native") XCTAssertEqual(configuration?.additionalConfiguration?["_dd.sdk_version"] as! String, SdkVersion) XCTAssertEqual(configuration?.configurationForTelemetry, nil) + XCTAssertEqual(configuration?.rumConfiguration?.nativeCrashReportEnabled, false) + XCTAssertEqual(configuration?.rumConfiguration?.nativeLongTaskThresholdMs, 200.0) XCTAssertEqual(configuration?.rumConfiguration?.nativeViewTracking, false) XCTAssertEqual(configuration?.rumConfiguration?.nativeInteractionTracking, false) XCTAssertEqual(configuration?.verbosity, nil) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 7551b312b..7dc8c6614 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -104,7 +104,7 @@ class DdSdkTests: XCTestCase { waitForExpectations(timeout: 0.5, handler: nil) } - + func testBuildConfigurationNoUIKitViewsByDefault() { let configuration: DdSdkConfiguration = .mockAny() @@ -468,7 +468,9 @@ class DdSdkTests: XCTestCase { func testBuildConfigurationNoCrashReportByDefault() { let core = MockDatadogCore() - let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: nil) + let rumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.nativeCrashReportEnabled = nil + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) DdSdkNativeInitialization().enableFeatures( sdkConfiguration: configuration @@ -479,7 +481,9 @@ class DdSdkTests: XCTestCase { func testBuildConfigurationNoCrashReport() { let core = MockDatadogCore() - let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: false) + let rumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.nativeCrashReportEnabled = false + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) DdSdkNativeInitialization().enableFeatures( sdkConfiguration: configuration @@ -493,7 +497,9 @@ class DdSdkTests: XCTestCase { CoreRegistry.register(default: core) defer { CoreRegistry.unregisterDefault() } - let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: true) + let rumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.nativeCrashReportEnabled = true + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) DdSdkNativeInitialization().enableFeatures( sdkConfiguration: configuration @@ -921,7 +927,9 @@ class DdSdkTests: XCTestCase { } func testBuildLongTaskThreshold() { - let configuration: DdSdkConfiguration = .mockAny(nativeLongTaskThresholdMs: 2500) + let rumConfiguration: RumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.nativeLongTaskThresholdMs = 2500 + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) let ddConfig = DdSdkNativeInitialization().buildRumConfiguration( configuration: configuration @@ -931,7 +939,9 @@ class DdSdkTests: XCTestCase { } func testBuildNoLongTaskTracking() { - let configuration: DdSdkConfiguration = .mockAny(nativeLongTaskThresholdMs: 0) + let rumConfiguration: RumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.nativeLongTaskThresholdMs = 0 + let configuration: DdSdkConfiguration = .mockAny(rumConfiguration: rumConfiguration) let ddConfig = DdSdkNativeInitialization().buildRumConfiguration( configuration: configuration @@ -941,13 +951,13 @@ class DdSdkTests: XCTestCase { } func testFirstPartyHosts() { - let traceConfiguration: TraceConfiguration = makeDefaultTraceConfiguration() - traceConfiguration.resourceTraceSampleRate = 66 + let rumConfiguration: RumConfiguration = makeDefaultRumConfiguration() + rumConfiguration.resourceTraceSampleRate = 66 let configuration: DdSdkConfiguration = .mockAny( firstPartyHosts: ([ ["match": "example.com", "propagatorTypes": ["datadog", "b3"]], ["match": "datadog.com", "propagatorTypes": ["b3multi", "tracecontext"]], - ] as NSArray).asFirstPartyHosts(), traceConfiguration: traceConfiguration) + ] as NSArray).asFirstPartyHosts(), rumConfiguration: rumConfiguration) let ddConfig = DdSdkNativeInitialization().buildRumConfiguration( configuration: configuration @@ -1503,10 +1513,10 @@ class DdSdkTests: XCTestCase { let rumConfiguration = makeDefaultRumConfiguration() rumConfiguration.longTaskThresholdMs = 0.1 + rumConfiguration.nativeCrashReportEnabled = false + rumConfiguration.nativeLongTaskThresholdMs = 0.0 let configuration: DdSdkConfiguration = DdSdkConfiguration.mockAny( - nativeCrashReportEnabled: false, - nativeLongTaskThresholdMs: 0.0, rumConfiguration: rumConfiguration, configurationForTelemetry: [ "initializationType": "LEGACY", "trackErrors": true, "trackInteractions": true, @@ -1723,8 +1733,11 @@ func makeDefaultRumConfiguration() -> RumConfiguration { trackFrustrations: nil, longTaskThresholdMs: 0.0, sessionSampleRate: 75.0, + resourceTraceSampleRate: 80.0, vitalsUpdateFrequency: nil, trackBackgroundEvents: nil, + nativeCrashReportEnabled: nil, + nativeLongTaskThresholdMs: nil, nativeViewTracking: nil, nativeInteractionTracking: nil, appHangThreshold: nil, @@ -1746,7 +1759,6 @@ func makeDefaultLogsConfiguration() -> LogsConfiguration { func makeDefaultTraceConfiguration() -> TraceConfiguration { TraceConfiguration( - resourceTraceSampleRate: 80.0, customEndpoint: nil ) } @@ -1759,8 +1771,6 @@ extension DdSdkConfiguration { site: NSString? = nil, service: NSString? = nil, verbosity: NSString? = nil, - nativeCrashReportEnabled: Bool? = nil, - nativeLongTaskThresholdMs: Double? = nil, trackingConsent: NSString? = "pending", uploadFrequency: NSString? = "AVERAGE", batchSize: NSString? = "MEDIUM", @@ -1779,8 +1789,6 @@ extension DdSdkConfiguration { site: site.asSite(), service: service, verbosity: verbosity, - nativeCrashReportEnabled: nativeCrashReportEnabled, - nativeLongTaskThresholdMs: nativeLongTaskThresholdMs, trackingConsent: trackingConsent.asTrackingConsent(), uploadFrequency: uploadFrequency.asUploadFrequency(), batchSize: batchSize.asBatchSize(), @@ -1799,6 +1807,7 @@ let DefaultRumConfigurationDict: NSDictionary = [ "applicationId": "app-id", "longTaskThresHoldMs": 0.0, "sessionSampleRate": 75.0, + "resourceTraceSampleRate": 80.0, "trackWatchdogTerminations": false, "trackMemoryWarnings": true, "telemetrySampleRate": 45.0, @@ -1809,9 +1818,7 @@ let DefaultLogsConfigurationDict: NSDictionary = [ "bundleLogsWithTraces": true, ] -let DefaultTraceConfigurationDict: NSDictionary = [ - "resourceTraceSampleRate": 80.0 -] +let DefaultTraceConfigurationDict: NSDictionary = [:] extension NSDictionary { static func mockAny( @@ -1821,8 +1828,6 @@ extension NSDictionary { site: NSString? = nil, service: NSString? = nil, verbosity: NSString? = nil, - nativeCrashReportEnabled: Bool? = nil, - nativeLongTaskThresholdMs: Double? = nil, trackingConsent: NSString? = "pending", uploadFrequency: NSString? = "AVERAGE", batchSize: NSString? = "MEDIUM", @@ -1845,8 +1850,6 @@ extension NSDictionary { config["site"] = site config["service"] = service config["verbosity"] = verbosity - config["nativeCrashReportEnabled"] = nativeCrashReportEnabled - config["nativeLongTaskThresholdMs"] = nativeLongTaskThresholdMs config["uploadFrequency"] = uploadFrequency config["batchSize"] = batchSize config["batchProcessingLevel"] = batchProcessingLevel @@ -1860,10 +1863,13 @@ extension NSDictionary { rumConfig["applicationId"] = rumConfiguration?["applicationId"] rumConfig["sesionSampleRate"] = rumConfiguration?["sessionSampleRate"] + rumConfig["resourceTraceSampleRate"] = rumConfiguration?["resourceTraceSampleRate"] rumConfig["longTaskThresholdMs"] = rumConfiguration?["longTaskThresholdMs"] rumConfig["telemetrySampleRate"] = rumConfiguration?["telemetrySampleRate"] rumConfig["vitalsUpdateFrequency"] = rumConfiguration?["vitalsUpdateFrequency"] rumConfig["trackBackgroundEvents"] = rumConfiguration?["trackBackgroundEvents"] + rumConfig["nativeCrashReportEnabled"] = rumConfiguration?["nativeCrashReportEnabled"] + rumConfig["nativeLongTaskThresholdMs"] = rumConfiguration?["nativeLongTaskThresholdMs"] rumConfig["nativeViewTracking"] = rumConfiguration?["nativeViewTracking"] rumConfig["nativeInteractionTracking"] = rumConfiguration?["nativeInteractionTracking"] rumConfig["customEndpoint"] = rumConfiguration?["customEndpoint"] @@ -1873,7 +1879,6 @@ extension NSDictionary { logsConfig["bundleLogsWithTraces"] = logsConfiguration?["bundleLogsWithTraces"] logsConfig["customEndpoint"] = logsConfiguration?["customEndpoint"] - traceConfig["resourceTraceSampleRate"] = traceConfiguration?["resourceTraceSampleRate"] traceConfig["customEndpoint"] = traceConfiguration?["customEndpoint"] return config diff --git a/packages/core/ios/Tests/Fixtures/complete-configuration.json b/packages/core/ios/Tests/Fixtures/complete-configuration.json index 8c61eda5b..263c6f0de 100644 --- a/packages/core/ios/Tests/Fixtures/complete-configuration.json +++ b/packages/core/ios/Tests/Fixtures/complete-configuration.json @@ -10,8 +10,6 @@ "version": "2.3.1", "useAccessibilityLabel": true, "batchSize": "SMALL", - "nativeCrashReportEnabled": true, - "nativeLongTaskThresholdMs": 333, "uploadFrequency": "FREQUENT", "rumConfiguration": { "applicationId": "fake-app-id", @@ -20,6 +18,8 @@ "trackErrors": true, "nativeViewTracking": true, "longTaskThresholdMs": 44, + "nativeCrashReportEnabled": true, + "nativeLongTaskThresholdMs": 333, "nativeInteractionTracking": true, "actionNameAttribute": "action-name-attr", "customEndpoint": "https://rum.example.com", @@ -27,6 +27,7 @@ "initialResourceThreshold": 0.5, "telemetrySampleRate": 60, "sessionSampleRate": 80, + "resourceTraceSampleRate": 33, "trackBackgroundEvents": true, "trackFrustrations": false }, @@ -34,7 +35,6 @@ "customEndpoint": "https://logs.example.com" }, "traceConfiguration": { - "resourceTraceSampleRate": 33, "customEndpoint": "https://trace.example.com" }, "firstPartyHosts": [ diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index dbc598ed6..bb0669a24 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -7,24 +7,26 @@ import { version as reactNativeVersion } from 'react-native/package.json'; import { InteractionManager } from 'react-native'; +import { InternalLog } from './InternalLog'; +import type { DatadogProviderConfiguration } from './config/DatadogProviderConfiguration'; +import { FileBasedConfiguration } from './config/FileBasedConfiguration'; import type { AutoInstrumentationConfiguration, - AutoInstrumentationParameters, - DatadogProviderConfiguration, - InitializationModeForTelemetry, - PartialInitializationConfiguration -} from './DdSdkReactNativeConfiguration'; -import { - DEFAULTS, - InitializationMode, - addDefaultValuesToAutoInstrumentationConfiguration, - buildConfigurationFromPartialConfiguration, - formatFirstPartyHosts, - CoreConfiguration -} from './DdSdkReactNativeConfiguration'; -import { InternalLog } from './InternalLog'; -import { SdkVerbosity } from './SdkVerbosity'; -import type { TrackingConsent } from './TrackingConsent'; + AutoInstrumentationParameters +} from './config/async/AutoInstrumentationConfiguration'; +import { addDefaultValuesToAutoInstrumentationConfiguration } from './config/async/AutoInstrumentationConfiguration'; +import type { PartialInitializationConfiguration } from './config/async/PartialInitializationConfiguration'; +import { buildConfigurationFromPartialConfiguration } from './config/async/asyncInitializationHelper'; +import { DdSdkNativeConfiguration } from './config/features/CoreConfigurationNative'; +import { CoreConfiguration } from './config/features/CoreConfiguration'; +import type { LogsNativeConfiguration } from './config/features/LogsConfigurationNative'; +import type { RumNativeConfiguration } from './config/features/RumConfigurationNative'; +import { RUM_DEFAULTS } from './config/features/RumConfiguration'; +import type { TraceNativeConfiguration } from './config/features/TraceConfigurationNative'; +import type { InitializationModeForTelemetry } from './config/types/InitializationModeForTelemetry'; +import { SdkVerbosity } from './config/types/SdkVerbosity'; +import type { TrackingConsent } from './config/types/TrackingConsent'; +import { InitializationMode } from './config/types'; import { DdLogs } from './logs/DdLogs'; import { DdRum } from './rum/DdRum'; import { DdRumErrorTracking } from './rum/instrumentation/DdRumErrorTracking'; @@ -37,15 +39,8 @@ import type { Attributes } from './sdk/AttributesSingleton/types'; import { registerNativeBridge } from './sdk/DatadogInternalBridge/DdSdkInternalNativeBridge'; import { BufferSingleton } from './sdk/DatadogProvider/Buffer/BufferSingleton'; import { NativeDdSdk } from './sdk/DdSdkInternal'; -import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { GlobalState } from './sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from './sdk/UserInfoSingleton/UserInfoSingleton'; -import { DdSdkNativeConfiguration } from './types'; -import type { - LogsNativeConfiguration, - RUMNativeConfiguration, - TraceNativeConfiguration -} from './types'; import { adaptLongTaskThreshold } from './utils/longTasksUtils'; import { version as sdkVersion } from './version'; @@ -396,6 +391,9 @@ export class DdSdkReactNative { initializationModeForTelemetry: InitializationModeForTelemetry; } ): DdSdkNativeConfiguration => { + if (configuration.additionalConfiguration === undefined) { + configuration.additionalConfiguration = {}; + } configuration.additionalConfiguration[DdSdkReactNative.DD_SOURCE_KEY] = 'react-native'; configuration.additionalConfiguration[ @@ -428,21 +426,29 @@ export class DdSdkReactNative { const rumConfiguration = configuration.rumConfiguration; if (rumConfiguration) { const longTaskThresholdMs = - configuration.rumConfiguration?.longTaskThresholdMs || - DEFAULTS.longTaskThresholdMs; + rumConfiguration.longTaskThresholdMs || + RUM_DEFAULTS.longTaskThresholdMs; rumConfiguration.longTaskThresholdMs = adaptLongTaskThreshold( longTaskThresholdMs ); + + const nativeLongTaskThresholdMs = + configuration.rumConfiguration?.nativeLongTaskThresholdMs ?? + RUM_DEFAULTS.nativeLongTaskThresholdMs; + rumConfiguration.nativeLongTaskThresholdMs = adaptLongTaskThreshold( + nativeLongTaskThresholdMs + ); } const trackInteractions = configuration.rumConfiguration?.trackInteractions || - DEFAULTS.trackInteractions; + RUM_DEFAULTS.trackInteractions; const trackResources = configuration.rumConfiguration?.trackResources || - DEFAULTS.trackResources; + RUM_DEFAULTS.trackResources; const trackErrors = - configuration.rumConfiguration?.trackErrors || DEFAULTS.trackErrors; + configuration.rumConfiguration?.trackErrors || + RUM_DEFAULTS.trackErrors; return new DdSdkNativeConfiguration( configuration.additionalConfiguration, @@ -451,16 +457,13 @@ export class DdSdkReactNative { configuration.site, configuration.service, configuration.verbosity, - configuration.nativeCrashReportEnabled, - adaptLongTaskThreshold(configuration.nativeLongTaskThresholdMs), configuration.trackingConsent, configuration.uploadFrequency, configuration.batchSize, configuration.batchProcessingLevel, configuration.proxyConfiguration, - formatFirstPartyHosts(configuration.firstPartyHosts), configuration.attributeEncoders, - rumConfiguration as RUMNativeConfiguration, + rumConfiguration as RumNativeConfiguration, configuration.logsConfiguration as LogsNativeConfiguration, configuration.traceConfiguration as TraceNativeConfiguration, { @@ -481,20 +484,22 @@ export class DdSdkReactNative { configuration: AutoInstrumentationParameters ) { const firstPartyHosts = - configuration.firstPartyHosts || DEFAULTS.getFirstPartyHosts(); + configuration.rumConfiguration?.firstPartyHosts || + RUM_DEFAULTS.getFirstPartyHosts(); const trackInteractions = configuration.rumConfiguration?.trackInteractions || - DEFAULTS.trackInteractions; + RUM_DEFAULTS.trackInteractions; const trackResources = configuration.rumConfiguration?.trackResources || - DEFAULTS.trackResources; + RUM_DEFAULTS.trackResources; const trackErrors = - configuration.rumConfiguration?.trackErrors || DEFAULTS.trackErrors; + configuration.rumConfiguration?.trackErrors || + RUM_DEFAULTS.trackErrors; const actionNameAttribute = configuration.rumConfiguration?.actionNameAttribute; const resourceTraceSampleRate = - configuration.traceConfiguration?.resourceTraceSampleRate || - DEFAULTS.resourceTraceSampleRate; + configuration.rumConfiguration?.resourceTraceSampleRate || + RUM_DEFAULTS.resourceTraceSampleRate; const logEventMapper = configuration.logsConfiguration?.logEventMapper; const errorEventMapper = configuration.rumConfiguration?.errorEventMapper; @@ -506,7 +511,9 @@ export class DdSdkReactNative { if (globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__) { DdBabelInteractionTracking.config = { trackInteractions, - useAccessibilityLabel: configuration.useAccessibilityLabel + useAccessibilityLabel: + configuration.rumConfiguration?.useAccessibilityLabel || + RUM_DEFAULTS.useAccessibilityLabel }; DdBabelInteractionTracking.attachRumInstance(DdRum); @@ -523,14 +530,15 @@ export class DdSdkReactNative { if (trackInteractions && !globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__) { DdRumUserInteractionTracking.startTracking({ actionNameAttribute, - useAccessibilityLabel: configuration.useAccessibilityLabel + useAccessibilityLabel: + configuration.rumConfiguration?.useAccessibilityLabel }); } if (trackResources) { DdRumResourceTracking.startTracking({ - tracingSamplingRate: resourceTraceSampleRate, - firstPartyHosts: formatFirstPartyHosts(firstPartyHosts) + resourceTraceSampleRate, + firstPartyHosts }); } diff --git a/packages/core/src/DdSdkReactNativeConfiguration.tsx b/packages/core/src/DdSdkReactNativeConfiguration.tsx deleted file mode 100644 index 1a25e14e9..000000000 --- a/packages/core/src/DdSdkReactNativeConfiguration.tsx +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -import type { ProxyConfiguration } from './ProxyConfiguration'; -import type { SdkVerbosity } from './SdkVerbosity'; -import { TrackingConsent } from './TrackingConsent'; -import type { ActionEventMapper } from './rum/eventMappers/actionEventMapper'; -import type { ErrorEventMapper } from './rum/eventMappers/errorEventMapper'; -import type { ResourceEventMapper } from './rum/eventMappers/resourceEventMapper'; -import type { FirstPartyHost } from './rum/types'; -import { PropagatorType } from './rum/types'; -import type { AttributeEncoder } from './sdk/AttributesEncoding/types'; -import type { LogEventMapper } from './types'; - -export enum VitalsUpdateFrequency { - FREQUENT = 'FREQUENT', - AVERAGE = 'AVERAGE', - RARE = 'RARE', - NEVER = 'NEVER' -} - -export enum UploadFrequency { - /** - * Upload data every 1000ms. - */ - FREQUENT = 'FREQUENT', - /** - * Upload data every 5000ms. - */ - AVERAGE = 'AVERAGE', - /** - * Upload data every 10000ms. - */ - RARE = 'RARE' -} - -export enum BatchSize { - /** - * Upload less frequent, larger batches of data - */ - LARGE = 'LARGE', - /** - * Use default size for batches of data - */ - MEDIUM = 'MEDIUM', - /** - * Upload more frequent, smaller batches of data - */ - SMALL = 'SMALL' -} - -export enum BatchProcessingLevel { - /** - * Only 1 batch will be sent in a single upload cycle. - */ - LOW = 'LOW', - /** - * 10 batches will be sent in a single upload cycle - */ - MEDIUM = 'MEDIUM', - /** - * 100 batches will be sent in a single upload cycle. - */ - HIGH = 'HIGH' -} - -export type FirstPartyHostsConfiguration = ( - | FirstPartyHost - | LegacyFirstPartyHost -)[]; - -export type LegacyFirstPartyHost = string; - -const isLegacyFirstPartyHost = ( - firstPartyHost: FirstPartyHost | LegacyFirstPartyHost -): firstPartyHost is LegacyFirstPartyHost => { - return typeof firstPartyHost === 'string'; -}; - -/** - * Defaults legacy first party hosts format to Datadog first party hosts to keep - * retro-compatibility before OTel support was introduced. - */ -export const formatFirstPartyHosts = ( - firstPartyHosts: FirstPartyHostsConfiguration -): FirstPartyHost[] => { - return firstPartyHosts.map(host => { - if (isLegacyFirstPartyHost(host)) { - return { - match: host, - propagatorTypes: [ - PropagatorType.DATADOG, - PropagatorType.TRACECONTEXT - ] - }; - } - return host; - }); -}; - -export const DEFAULTS = { - nativeCrashReportEnabled: false, - sessionSampleRate: 100.0, - resourceTraceSampleRate: 100.0, - site: 'US1', - longTaskThresholdMs: 0, - nativeLongTaskThresholdMs: 200, - nativeViewTracking: false, - nativeInteractionTracking: false, - getFirstPartyHosts: () => [], - getAdditionalConfiguration: () => ({}), - trackingConsent: TrackingConsent.GRANTED, - telemetrySampleRate: 20.0, - vitalsUpdateFrequency: VitalsUpdateFrequency.AVERAGE, - logEventMapper: null, - errorEventMapper: null, - resourceEventMapper: null, - actionEventMapper: null, - trackFrustrations: true, - uploadFrequency: UploadFrequency.AVERAGE, - batchSize: BatchSize.MEDIUM, - trackBackgroundEvents: false, - bundleLogsWithRum: true, - bundleLogsWithTraces: true, - useAccessibilityLabel: true, - trackWatchdogTerminations: false, - batchProcessingLevel: BatchProcessingLevel.MEDIUM, - trackMemoryWarnings: true, - trackInteractions: false, - trackResources: false, - trackErrors: false -}; - -/** - * The Core configuration class. - * It will be used to configure the SDK functionality at initialization. - */ -export class CoreConfiguration { - public additionalConfiguration: { - [k: string]: any; - } = DEFAULTS.getAdditionalConfiguration(); - - /** - * Defines the Datadog SDK policy when batching data together before uploading it to Datadog servers. - * Smaller batches mean smaller but more network requests, whereas larger batches mean fewer but larger network requests. - */ - public batchSize: BatchSize = DEFAULTS.batchSize; - - /** - * Sets the preferred level for processing batches of data. - */ - public batchProcessingLevel: BatchProcessingLevel = - BatchProcessingLevel.MEDIUM; - - /** - * Enables crash reporting for native platforms (iOS, Android). Default `false`. - */ - public nativeCrashReportEnabled: boolean = - DEFAULTS.nativeCrashReportEnabled; - - /** - * The threshold for native long tasks reporting in milliseconds. - * - * - Setting it to `0` or `false` disables native long task reporting. - * - Values below `100` will be raised to `100`. - * - Values above `5000` will be lowered to `5000`. - * - * Default value is `200`. - */ - public nativeLongTaskThresholdMs: number | false = - DEFAULTS.nativeLongTaskThresholdMs; - - public proxyConfiguration?: ProxyConfiguration = undefined; - - public service?: string = undefined; - - /** - * Sets the preferred frequency for uploading batches of data. - */ - public uploadFrequency: UploadFrequency = DEFAULTS.uploadFrequency; - - /** - * Verbosity for internal SDK logging. - * Set to `SdkVerbosity.DEBUG` to debug your SDK implementation. - */ - public verbosity?: SdkVerbosity = undefined; - - /** - * Overrides the reported version of the app. - * Accepted characters are alphanumerics and `_`, `-`, `:`, `.`, `/`. - * Other special characters are converted to underscores. - * - * See https://docs.datadoghq.com/getting_started/tagging/#define-tags for more information on the format. - * - * Make sure you set it correctly, as it will have to match the one specified during the upload of your source maps and other mapping files. - */ - public version?: string; - - /** - * Add a suffix to the reported version of the app. - * Accepted characters are alphanumerics and `_`, `-`, `:`, `.`, `/`. - * Other special characters are converted to underscores. - * - * See https://docs.datadoghq.com/getting_started/tagging/#define-tags for more information on the format. - * - * A dash (`-`) will be automatically added between the version and the suffix - */ - public versionSuffix?: string; - - public site: string = DEFAULTS.site; - - /** - * List of your backends hosts to enable tracing with. - * Regular expressions are NOT supported. - * - * Matches domains and subdomains, e.g. `['example.com']` matches `example.com` and `api.example.com`. - */ - public firstPartyHosts: FirstPartyHostsConfiguration = DEFAULTS.getFirstPartyHosts(); - - /** - * Optional list of custom encoders for attributes. - * - * Each encoder defines how to detect (`check`) and transform (`encode`) - * values of a specific type that is not handled by the built-in encoders - * (e.g., domain-specific objects, custom classes). - * - * These encoders are applied before the built-in ones. If an encoder - * successfully `check` a value, its `encode` result will be used. - * - * Example use cases: - * - Serializing a custom `UUID` class into a string - * - Handling third-party library objects that are not JSON-serializable - */ - public attributeEncoders: AttributeEncoder[] = []; - - public rumConfiguration?: RumConfiguration; - - public logsConfiguration?: LogsConfiguration; - - public traceConfiguration?: TraceConfiguration; - - constructor( - readonly clientToken: string, - readonly env: string, - readonly trackingConsent: TrackingConsent = DEFAULTS.trackingConsent, - readonly useAccessibilityLabel: boolean = DEFAULTS.useAccessibilityLabel // eslint-disable-next-line no-empty-function - ) {} -} - -export class RumConfiguration { - public actionEventMapper: ActionEventMapper | null = - DEFAULTS.actionEventMapper; - - /** - * Specifies a custom prop to name RUM actions on elements having an `onPress` prop. - * - * For example if you set it to `testID`, the value of the `testID` prop is used as a custom action name: - * - * ```js - * dismiss()}> - * ``` - * - * `dd-action-name` is favored when both attributes are present on an element. - */ - public actionNameAttribute?: string; - - /** - * The app hang threshold in seconds for non-fatal app hangs on iOS. - * - * App hangs are an iOS-specific type of error that happens when the application is unresponsive for too long. - * By default, app hangs reporting is disabled, but you can enable it and set your - * own threshold to monitor app hangs that last more than a specified - * duration by using the this parameter. - * - * Set the `appHangThreshold` parameter to the minimal duration you want - * app hangs to be reported. For example, enter 0.25 to report hangs lasting at least 250 ms. - * See [Configure the app hang threshold](https://docs.datadoghq.com/real_user_monitoring/error_tracking/mobile/ios/?tab=cocoapods#configure-the-app-hang-threshold) - * for more guidance on what to set this value to. - */ - public appHangThreshold?: number; - - public errorEventMapper: ErrorEventMapper | null = - DEFAULTS.errorEventMapper; - - /** - * The amount of time after a view starts where a Resource should be - * considered when calculating Time to Network-Settled (TNS). TNS will be - * calculated using all resources that start withing the specified threshold, in seconds. - * Defaults to 0.1 seconds. - */ - public initialResourceThreshold?: number; - - /** - * The threshold for javascript long tasks reporting in milliseconds. - * - * - Setting it to `0` or `false` disables javascript long task reporting. - * - Values below `100` will be raised to `100`. - * - Values above `5000` will be lowered to `5000`. - * - * Default value is `0` - */ - public longTaskThresholdMs: number | false = - DEFAULTS.nativeLongTaskThresholdMs; - - /** - * Enables tracking of memory warnings as RUM events. - * - * When enabled, the SDK will automatically record a RUM event each time the app - * receives a memory warning from the operating system. - * - * **Note:** This setting is only supported on **iOS**. It has no effect on other platforms. - */ - public trackMemoryWarnings: boolean = DEFAULTS.trackMemoryWarnings; - - /** - * Enables native views tracking. - * Set to `true` if you use a custom navigation system relying on native views. - */ - public nativeViewTracking: boolean = DEFAULTS.nativeViewTracking; - - /** - * Enables native interaction tracking. - * Set to `true` if you want to track interactions on native screens. - */ - public nativeInteractionTracking: boolean = - DEFAULTS.nativeInteractionTracking; - - public resourceEventMapper: ResourceEventMapper | null = - DEFAULTS.resourceEventMapper; - - /** - * Percentage of sampled RUM sessions. Range `0`-`100`. - * Default is `100`. - */ - public sessionSampleRate: number = DEFAULTS.sessionSampleRate; - - /** - * Enables tracking of RUM event when no RUM View is active. - * - * By default, background events are not tracked. Enabling this feature might increase the - * number of sessions tracked and impact your billing. - */ - public trackBackgroundEvents: boolean = DEFAULTS.trackBackgroundEvents; - - /** - * Enables tracking of frustration signals (error taps). Defaults to `true`. - */ - public trackFrustrations: boolean = DEFAULTS.trackFrustrations; - - /** - * Enables tracking of non-fatal ANRs on Android. - * By default, the reporting of non-fatal ANRs on Android 30+ is disabled because it would - * create too much noise over fatal ANRs. On Android 29 and below, however, - * the reporting of non-fatal ANRs is enabled by default, - * as fatal ANRs cannot be reported on those versions. - */ - public trackNonFatalAnrs?: boolean; - - /** - * Determines whether the SDK should track application termination by the watchdog on iOS. Default: `false`. - */ - public trackWatchdogTerminations: boolean = - DEFAULTS.trackWatchdogTerminations; - - /** - * Sets the preferred frequency for collecting mobile vitals. - */ - public vitalsUpdateFrequency: VitalsUpdateFrequency = - DEFAULTS.vitalsUpdateFrequency; - - /** - * Sets a target custom server for RUM. - */ - public customEndpoint?: string; - - /** - * The sampling rate for Internal Telemetry (info related to the work of the - * SDK internals). - * - * The sampling rate must be a value between 0 and 100. A value of 0 means no - * telemetry will be sent, 100 means all telemetry will be sent. When - * `telemetrySampleRate` is not set, the default value from the iOS and - * Android SDK is used, which is 20. - */ - public telemetrySampleRate: number = DEFAULTS.telemetrySampleRate; - - constructor( - readonly applicationId: string, - readonly trackInteractions: boolean = DEFAULTS.trackInteractions, - readonly trackResources: boolean = DEFAULTS.trackResources, - readonly trackErrors: boolean = DEFAULTS.trackErrors // eslint-disable-next-line no-empty-function - ) {} -} -export class LogsConfiguration { - /** - * Enables RUM correlation with logs. - * - * By default, RUM is enabled for logs. - */ - public bundleLogsWithRum: boolean = DEFAULTS.bundleLogsWithRum; - - /** - * Enables Traces correlation with logs. - * - * By default, Traces is enabled for logs. - */ - public bundleLogsWithTraces: boolean = DEFAULTS.bundleLogsWithTraces; - - /** - * Sets a target custom server for Logs. - */ - public customEndpoint?: string; - - public logEventMapper: LogEventMapper | null = DEFAULTS.logEventMapper; -} - -export class TraceConfiguration { - /** - * Percentage of tracing integrations for network calls between your app and your backend. Range `0`-`100`. - * Default is `100`. - */ - public resourceTraceSampleRate: number = DEFAULTS.resourceTraceSampleRate; - - /** - * Sets a target custom server for Traces. - */ - public customEndpoint?: string; -} - -export class DatadogProviderConfiguration extends CoreConfiguration { - public initializationMode: InitializationMode = InitializationMode.SYNC; -} - -/** - * Auto Instrumentation configuration passed to DatadogProvider. - * Does not include default values. - */ -export type AutoInstrumentationConfiguration = { - readonly useAccessibilityLabel?: boolean; - readonly firstPartyHosts?: FirstPartyHostsConfiguration; - readonly rumConfiguration: { - readonly trackInteractions: boolean; - readonly trackResources: boolean; - readonly trackErrors: boolean; - readonly actionNameAttribute?: string; - readonly actionEventMapper?: ActionEventMapper | null; - readonly errorEventMapper?: ErrorEventMapper | null; - readonly resourceEventMapper?: ResourceEventMapper | null; - }; - readonly logsConfiguration: { - readonly logEventMapper?: LogEventMapper | null; - }; - readonly traceConfiguration: { - readonly resourceTraceSampleRate?: number; - }; -}; - -/** - * Parameters needed to start auto instrumentation. Includes default values. - */ -export type AutoInstrumentationParameters = { - readonly useAccessibilityLabel: boolean; - readonly firstPartyHosts: FirstPartyHostsConfiguration; - readonly rumConfiguration?: { - readonly trackInteractions: boolean; - readonly trackResources: boolean; - readonly trackErrors: boolean; - readonly actionNameAttribute?: string; - readonly actionEventMapper: ActionEventMapper | null; - readonly errorEventMapper: ErrorEventMapper | null; - readonly resourceEventMapper: ResourceEventMapper | null; - }; - readonly logsConfiguration?: { - readonly logEventMapper: LogEventMapper | null; - }; - readonly traceConfiguration?: { - readonly resourceTraceSampleRate: number; - }; -}; - -/** - * We could use `Proxy` instead of this function, but `Proxy` is not available on - * the older android jsc that can still be used. - */ -export const addDefaultValuesToAutoInstrumentationConfiguration = ( - features: AutoInstrumentationConfiguration -): AutoInstrumentationParameters => { - return { - useAccessibilityLabel: DEFAULTS.useAccessibilityLabel, - firstPartyHosts: - features.firstPartyHosts || DEFAULTS.getFirstPartyHosts(), - rumConfiguration: { - trackInteractions: features.rumConfiguration.trackInteractions, - trackResources: features.rumConfiguration.trackResources, - trackErrors: features.rumConfiguration.trackErrors, - actionNameAttribute: features.rumConfiguration.actionNameAttribute, - errorEventMapper: - features.rumConfiguration.errorEventMapper === undefined - ? DEFAULTS.errorEventMapper - : features.rumConfiguration.errorEventMapper, - resourceEventMapper: - features.rumConfiguration.resourceEventMapper === undefined - ? DEFAULTS.resourceEventMapper - : features.rumConfiguration.resourceEventMapper, - actionEventMapper: - features.rumConfiguration.actionEventMapper === undefined - ? DEFAULTS.actionEventMapper - : features.rumConfiguration.actionEventMapper - }, - traceConfiguration: { - resourceTraceSampleRate: - features.traceConfiguration.resourceTraceSampleRate === - undefined - ? DEFAULTS.resourceTraceSampleRate - : features.traceConfiguration.resourceTraceSampleRate - }, - logsConfiguration: { - logEventMapper: - features.logsConfiguration.logEventMapper === undefined - ? DEFAULTS.logEventMapper - : features.logsConfiguration.logEventMapper - } - }; -}; - -export type PartialInitializationConfiguration = { - readonly additionalConfiguration?: { [k: string]: any }; - readonly attributeEncoders?: AttributeEncoder[]; - readonly clientToken: string; - readonly env: string; - readonly site?: string; - readonly trackingConsent?: TrackingConsent; - readonly verbosity?: SdkVerbosity | undefined; - readonly service?: string; - readonly version?: string; - versionSuffix?: string; - readonly proxyConfiguration?: ProxyConfiguration; - readonly nativeLongTaskThresholdMs?: number | false; - readonly nativeCrashReportEnabled?: boolean; - readonly uploadFrequency?: UploadFrequency; - readonly batchSize?: BatchSize; - readonly batchProcessingLevel?: BatchProcessingLevel; - readonly rumConfiguration?: { - readonly applicationId: string; - readonly sessionSampleRate?: number; - readonly nativeViewTracking?: boolean; - readonly nativeInteractionTracking?: boolean; - readonly longTaskThresholdMs?: number | false; - readonly vitalsUpdateFrequency?: VitalsUpdateFrequency; - readonly trackFrustrations?: boolean; - readonly trackBackgroundEvents?: boolean; - readonly initialResourceThreshold?: number; - readonly trackMemoryWarnings?: boolean; - readonly telemetrySampleRate?: number; - readonly customEndpoint?: string; - }; - readonly logsConfiguration?: { - readonly bundleLogsWithRum?: boolean; - readonly bundleLogsWithTraces?: boolean; - readonly customEndpoint?: string; - }; - readonly traceConfiguration?: { - readonly customEndpoint?: string; - }; -}; - -export const buildConfigurationFromPartialConfiguration = ( - features: AutoInstrumentationConfiguration, - configuration: PartialInitializationConfiguration -): CoreConfiguration => { - const { clientToken, env } = configuration; - - const SdkConfiguration = new CoreConfiguration( - clientToken, - env, - configuration.trackingConsent, - features.useAccessibilityLabel - ); - - if (configuration.additionalConfiguration) { - SdkConfiguration.additionalConfiguration = - configuration.additionalConfiguration; - } - - if (configuration.batchProcessingLevel) { - SdkConfiguration.batchProcessingLevel = - configuration.batchProcessingLevel; - } - - if (configuration.batchSize) { - SdkConfiguration.batchSize = configuration.batchSize; - } - - if (configuration.nativeCrashReportEnabled) { - SdkConfiguration.nativeCrashReportEnabled = - configuration.nativeCrashReportEnabled; - } - - if (configuration.nativeLongTaskThresholdMs) { - SdkConfiguration.nativeLongTaskThresholdMs = - configuration.nativeLongTaskThresholdMs; - } - - if (configuration.proxyConfiguration) { - SdkConfiguration.proxyConfiguration = configuration.proxyConfiguration; - } - - if (configuration.service) { - SdkConfiguration.service = configuration.service; - } - - if (configuration.site) { - SdkConfiguration.site = configuration.site; - } - - if (configuration.uploadFrequency) { - SdkConfiguration.uploadFrequency = configuration.uploadFrequency; - } - - if (configuration.verbosity) { - SdkConfiguration.verbosity = configuration.verbosity; - } - - if (configuration.version) { - SdkConfiguration.version = configuration.version; - } - - if (configuration.versionSuffix) { - SdkConfiguration.versionSuffix = configuration.versionSuffix; - } - - if (features.firstPartyHosts) { - SdkConfiguration.firstPartyHosts = features.firstPartyHosts; - } - - if (configuration.attributeEncoders) { - SdkConfiguration.attributeEncoders = configuration.attributeEncoders; - } - - if (configuration.rumConfiguration?.applicationId !== undefined) { - SdkConfiguration.rumConfiguration = new RumConfiguration( - configuration.rumConfiguration.applicationId, - features.rumConfiguration.trackInteractions, - features.rumConfiguration.trackResources, - features.rumConfiguration.trackErrors - ); - } - - if (SdkConfiguration.rumConfiguration) { - if (features.rumConfiguration.errorEventMapper) { - SdkConfiguration.rumConfiguration.errorEventMapper = - features.rumConfiguration.errorEventMapper; - } - - if (features.rumConfiguration.resourceEventMapper) { - SdkConfiguration.rumConfiguration.resourceEventMapper = - features.rumConfiguration.resourceEventMapper; - } - - if (features.rumConfiguration.actionEventMapper) { - SdkConfiguration.rumConfiguration.actionEventMapper = - features.rumConfiguration.actionEventMapper; - } - - if (features.rumConfiguration.actionNameAttribute) { - SdkConfiguration.rumConfiguration.actionNameAttribute = - features.rumConfiguration.actionNameAttribute; - } - - if (configuration.rumConfiguration?.initialResourceThreshold) { - SdkConfiguration.rumConfiguration.initialResourceThreshold = - configuration.rumConfiguration?.initialResourceThreshold; - } - - if (configuration.rumConfiguration?.longTaskThresholdMs) { - SdkConfiguration.rumConfiguration.longTaskThresholdMs = - configuration.rumConfiguration?.longTaskThresholdMs; - } - - if (configuration.rumConfiguration?.nativeInteractionTracking) { - SdkConfiguration.rumConfiguration.nativeInteractionTracking = - configuration.rumConfiguration?.nativeInteractionTracking; - } - - if (configuration.rumConfiguration?.nativeViewTracking) { - SdkConfiguration.rumConfiguration.nativeViewTracking = - configuration.rumConfiguration?.nativeViewTracking; - } - - if (configuration.rumConfiguration?.sessionSampleRate) { - SdkConfiguration.rumConfiguration.sessionSampleRate = - configuration.rumConfiguration?.sessionSampleRate; - } - - if (configuration.rumConfiguration?.telemetrySampleRate) { - SdkConfiguration.rumConfiguration.telemetrySampleRate = - configuration.rumConfiguration?.telemetrySampleRate; - } - - if (configuration.rumConfiguration?.trackBackgroundEvents) { - SdkConfiguration.rumConfiguration.trackBackgroundEvents = - configuration.rumConfiguration?.trackBackgroundEvents; - } - - if (configuration.rumConfiguration?.trackFrustrations) { - SdkConfiguration.rumConfiguration.trackFrustrations = - configuration.rumConfiguration?.trackFrustrations; - } - - if (configuration.rumConfiguration?.trackMemoryWarnings) { - SdkConfiguration.rumConfiguration.trackMemoryWarnings = - configuration.rumConfiguration?.trackMemoryWarnings; - } - - if (configuration.rumConfiguration?.vitalsUpdateFrequency) { - SdkConfiguration.rumConfiguration.vitalsUpdateFrequency = - configuration.rumConfiguration?.vitalsUpdateFrequency; - } - - if (configuration.rumConfiguration?.customEndpoint) { - SdkConfiguration.rumConfiguration.customEndpoint = - configuration.rumConfiguration?.customEndpoint; - } - } - - if (features.traceConfiguration !== undefined) { - SdkConfiguration.traceConfiguration = new TraceConfiguration(); - - if ( - features.traceConfiguration?.resourceTraceSampleRate !== undefined - ) { - SdkConfiguration.traceConfiguration.resourceTraceSampleRate = - features.traceConfiguration.resourceTraceSampleRate; - } - - if (configuration.traceConfiguration?.customEndpoint) { - SdkConfiguration.traceConfiguration.customEndpoint = - configuration.traceConfiguration?.customEndpoint; - } - } - - if (features.logsConfiguration !== undefined) { - SdkConfiguration.logsConfiguration = new LogsConfiguration(); - - if (features.logsConfiguration.logEventMapper) { - SdkConfiguration.logsConfiguration.logEventMapper = - features.logsConfiguration.logEventMapper; - } - - if (configuration.logsConfiguration?.bundleLogsWithRum) { - SdkConfiguration.logsConfiguration.bundleLogsWithRum = - configuration.logsConfiguration?.bundleLogsWithRum; - } - - if (configuration.logsConfiguration?.bundleLogsWithTraces) { - SdkConfiguration.logsConfiguration.bundleLogsWithTraces = - configuration.logsConfiguration?.bundleLogsWithTraces; - } - - if (configuration.logsConfiguration?.customEndpoint) { - SdkConfiguration.logsConfiguration.customEndpoint = - configuration.traceConfiguration?.customEndpoint; - } - } - - return SdkConfiguration; -}; - -export enum InitializationMode { - SYNC = 'SYNC', - ASYNC = 'ASYNC' -} - -export type InitializationModeForTelemetry = - | 'LEGACY' - | 'SYNC' - | 'ASYNC' - | 'PARTIAL' - | 'FILE'; diff --git a/packages/core/src/InternalLog.tsx b/packages/core/src/InternalLog.tsx index 3a41d13ab..eb755efdd 100644 --- a/packages/core/src/InternalLog.tsx +++ b/packages/core/src/InternalLog.tsx @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -import { SdkVerbosity } from './SdkVerbosity'; +import { SdkVerbosity } from './config/types/SdkVerbosity'; export const DATADOG_MESSAGE_PREFIX = 'DATADOG:'; diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index 72341c0d6..d9abeafe2 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -7,16 +7,14 @@ import { version as reactNativeVersion } from 'react-native/package.json'; import { NativeModules } from 'react-native'; -import { - CoreConfiguration, - LogsConfiguration, - RumConfiguration, - TraceConfiguration -} from '../DdSdkReactNativeConfiguration'; import { DdSdkReactNative } from '../DdSdkReactNative'; -import { ProxyConfiguration, ProxyType } from '../ProxyConfiguration'; -import { SdkVerbosity } from '../SdkVerbosity'; -import { TrackingConsent } from '../TrackingConsent'; +import type { DdSdkNativeConfiguration } from '../config/features/CoreConfigurationNative'; +import { CoreConfiguration } from '../config/features/CoreConfiguration'; +import { LogsConfiguration } from '../config/features/LogsConfiguration'; +import { RumConfiguration } from '../config/features/RumConfiguration'; +import { TraceConfiguration } from '../config/features/TraceConfiguration'; +import { TrackingConsent } from '../config/types/TrackingConsent'; +import { ProxyConfiguration, ProxyType, SdkVerbosity } from '../config/types'; import { DdLogs } from '../logs/DdLogs'; import { DdRum } from '../rum/DdRum'; import { DdRumErrorTracking } from '../rum/instrumentation/DdRumErrorTracking'; @@ -27,8 +25,8 @@ import { AttributesSingleton } from '../sdk/AttributesSingleton/AttributesSingle import { NativeDdSdk } from '../sdk/DdSdkInternal'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from '../sdk/UserInfoSingleton/UserInfoSingleton'; +import type { LogEvent } from '../types'; import { ErrorSource } from '../types'; -import type { DdSdkNativeConfiguration } from '../types'; import { version as sdkVersion } from '../version'; jest.mock('../InternalLog'); @@ -123,7 +121,9 @@ describe('DdSdkReactNative', () => { expect( ddSdkConfiguration.rumConfiguration?.nativeViewTracking ).toBe(false); - expect(ddSdkConfiguration.firstPartyHosts).toEqual([]); + expect( + ddSdkConfiguration.rumConfiguration?.firstPartyHosts + ).toEqual([]); expect( ddSdkConfiguration.logsConfiguration?.bundleLogsWithRum ).toBe(true); @@ -616,10 +616,15 @@ describe('DdSdkReactNative', () => { false, true ); - configuration.traceConfiguration = new TraceConfiguration(); - configuration.traceConfiguration.resourceTraceSampleRate = 42; - configuration.firstPartyHosts = [ - 'api.example.com', + configuration.rumConfiguration.resourceTraceSampleRate = 42; + configuration.rumConfiguration.firstPartyHosts = [ + { + match: 'api.example.com', + propagatorTypes: [ + PropagatorType.DATADOG, + PropagatorType.TRACECONTEXT + ] + }, { match: 'something.fr', propagatorTypes: [PropagatorType.DATADOG] @@ -640,7 +645,9 @@ describe('DdSdkReactNative', () => { fakeAppId ); expect(ddSdkConfiguration.env).toBe(fakeEnvName); - expect(ddSdkConfiguration.firstPartyHosts).toEqual([ + expect( + ddSdkConfiguration.rumConfiguration?.firstPartyHosts + ).toEqual([ { match: 'api.example.com', propagatorTypes: ['datadog', 'tracecontext'] @@ -659,7 +666,7 @@ describe('DdSdkReactNative', () => { 1 ); expect(DdRumResourceTracking.startTracking).toHaveBeenCalledWith({ - tracingSamplingRate: 42, + resourceTraceSampleRate: 42, firstPartyHosts: [ { match: 'api.example.com', @@ -688,9 +695,7 @@ describe('DdSdkReactNative', () => { false, true ); - configuration.traceConfiguration = new TraceConfiguration(); - configuration.traceConfiguration.resourceTraceSampleRate = 2; - + configuration.rumConfiguration.resourceTraceSampleRate = 2; NativeModules.DdSdk.initialize.mockResolvedValue(null); // WHEN @@ -728,11 +733,12 @@ describe('DdSdkReactNative', () => { false, true ); - configuration.logsConfiguration = new LogsConfiguration(); - configuration.logsConfiguration.logEventMapper = log => { - log.message = 'new message'; - return log; - }; + configuration.logsConfiguration = new LogsConfiguration({ + logEventMapper: (log: LogEvent) => { + log.message = 'new message'; + return log; + } + }); NativeModules.DdSdk.initialize.mockResolvedValue(null); @@ -1066,7 +1072,7 @@ describe('DdSdkReactNative', () => { false, true ); - configuration.nativeLongTaskThresholdMs = 234; + configuration.rumConfiguration.nativeLongTaskThresholdMs = 234; configuration.rumConfiguration.longTaskThresholdMs = 456; NativeModules.DdSdk.initialize.mockResolvedValue(null); @@ -1077,7 +1083,9 @@ describe('DdSdkReactNative', () => { // THEN const ddSdkConfiguration = NativeModules.DdSdk.initialize.mock .calls[0][0] as DdSdkNativeConfiguration; - expect(ddSdkConfiguration.nativeLongTaskThresholdMs).toBe(234); + expect( + ddSdkConfiguration.rumConfiguration?.nativeLongTaskThresholdMs + ).toBe(234); expect( ddSdkConfiguration.rumConfiguration?.longTaskThresholdMs ).toBe(456); @@ -1098,7 +1106,7 @@ describe('DdSdkReactNative', () => { false, true ); - configuration.nativeLongTaskThresholdMs = false; + configuration.rumConfiguration.nativeLongTaskThresholdMs = 0; configuration.rumConfiguration.longTaskThresholdMs = false; NativeModules.DdSdk.initialize.mockResolvedValue(null); @@ -1109,7 +1117,9 @@ describe('DdSdkReactNative', () => { // THEN const ddSdkConfiguration = NativeModules.DdSdk.initialize.mock .calls[0][0] as DdSdkNativeConfiguration; - expect(ddSdkConfiguration.nativeLongTaskThresholdMs).toBe(0); + expect( + ddSdkConfiguration.rumConfiguration?.nativeLongTaskThresholdMs + ).toBe(0); expect( ddSdkConfiguration.rumConfiguration?.longTaskThresholdMs ).toBe(0); diff --git a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts index 56c9a05d4..207d9abdd 100644 --- a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts +++ b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts @@ -4,14 +4,16 @@ * Copyright 2016-Present Datadog, Inc. */ +import { buildConfigurationFromPartialConfiguration } from '../config/async/asyncInitializationHelper'; import { BatchSize, - UploadFrequency, - buildConfigurationFromPartialConfiguration -} from '../DdSdkReactNativeConfiguration'; -import { ProxyConfiguration, ProxyType } from '../ProxyConfiguration'; -import { SdkVerbosity } from '../SdkVerbosity'; -import { TrackingConsent } from '../TrackingConsent'; + ProxyConfiguration, + ProxyType, + SdkVerbosity, + TrackingConsent, + UploadFrequency +} from '../config/types'; +import { PropagatorType } from '../rum/types'; describe('DdSdkReactNativeConfiguration', () => { describe('buildConfigurationFromPartialConfiguration', () => { @@ -24,15 +26,16 @@ describe('DdSdkReactNativeConfiguration', () => { trackInteractions: false, trackResources: false }, - logsConfiguration: {}, - traceConfiguration: {} + logsConfiguration: {} }, { clientToken: 'fake-client-token', env: 'fake-env', rumConfiguration: { applicationId: 'fake-app-id' - } + }, + logsConfiguration: {}, + traceConfiguration: {} } ) ).toMatchInlineSnapshot(` @@ -43,23 +46,29 @@ describe('DdSdkReactNativeConfiguration', () => { "batchSize": "MEDIUM", "clientToken": "fake-client-token", "env": "fake-env", - "firstPartyHosts": [], "logsConfiguration": LogsConfiguration { "bundleLogsWithRum": true, "bundleLogsWithTraces": true, + "customEndpoint": undefined, "logEventMapper": null, }, - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, + "actionNameAttribute": undefined, + "appHangThreshold": undefined, "applicationId": "fake-app-id", + "customEndpoint": undefined, "errorEventMapper": null, - "longTaskThresholdMs": 200, + "firstPartyHosts": [], + "initialResourceThreshold": undefined, + "longTaskThresholdMs": 0, + "nativeCrashReportEnabled": false, "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 200, "nativeViewTracking": false, "resourceEventMapper": null, + "resourceTraceSampleRate": 100, "sessionSampleRate": 100, "telemetrySampleRate": 20, "trackBackgroundEvents": false, @@ -67,19 +76,22 @@ describe('DdSdkReactNativeConfiguration', () => { "trackFrustrations": true, "trackInteractions": false, "trackMemoryWarnings": true, + "trackNonFatalAnrs": undefined, "trackResources": false, "trackWatchdogTerminations": false, + "useAccessibilityLabel": true, "vitalsUpdateFrequency": "AVERAGE", }, "service": undefined, "site": "US1", "traceConfiguration": TraceConfiguration { - "resourceTraceSampleRate": 100, + "customEndpoint": undefined, }, "trackingConsent": "granted", "uploadFrequency": "AVERAGE", - "useAccessibilityLabel": true, "verbosity": undefined, + "version": undefined, + "versionSuffix": undefined, } `); }); @@ -90,30 +102,40 @@ describe('DdSdkReactNativeConfiguration', () => { { rumConfiguration: { actionNameAttribute: 'testID', + useAccessibilityLabel: true, trackErrors: true, trackInteractions: true, trackResources: true, + resourceTraceSampleRate: 80, + firstPartyHosts: [ + { + match: 'api.com', + propagatorTypes: [ + PropagatorType.DATADOG, + PropagatorType.TRACECONTEXT + ] + } + ], + errorEventMapper: event => event, resourceEventMapper: event => event, actionEventMapper: event => event }, - traceConfiguration: { - resourceTraceSampleRate: 80 - }, logsConfiguration: { logEventMapper: event => event - }, - firstPartyHosts: ['api.com'], - useAccessibilityLabel: true + } }, { rumConfiguration: { applicationId: 'fake-app-id', sessionSampleRate: 80, + nativeCrashReportEnabled: true, + nativeLongTaskThresholdMs: 345, nativeViewTracking: true, nativeInteractionTracking: true, longTaskThresholdMs: 567, trackFrustrations: true, + trackNonFatalAnrs: true, trackBackgroundEvents: true, customEndpoint: 'https://rum.example.com/', initialResourceThreshold: 0.123 @@ -142,8 +164,6 @@ describe('DdSdkReactNativeConfiguration', () => { additionalField: 'fake-value' }, trackingConsent: TrackingConsent.PENDING, - nativeCrashReportEnabled: true, - nativeLongTaskThresholdMs: 345, uploadFrequency: UploadFrequency.FREQUENT, batchSize: BatchSize.LARGE } @@ -158,17 +178,12 @@ describe('DdSdkReactNativeConfiguration', () => { "batchSize": "LARGE", "clientToken": "fake-client-token", "env": "fake-env", - "firstPartyHosts": [ - "api.com", - ], "logsConfiguration": LogsConfiguration { "bundleLogsWithRum": true, "bundleLogsWithTraces": true, - "customEndpoint": "https://trace.example.com/", + "customEndpoint": "https://logs.example.com/", "logEventMapper": [Function], }, - "nativeCrashReportEnabled": true, - "nativeLongTaskThresholdMs": 345, "proxyConfiguration": ProxyConfiguration { "address": "api.com", "port": 443, @@ -177,14 +192,27 @@ describe('DdSdkReactNativeConfiguration', () => { "rumConfiguration": RumConfiguration { "actionEventMapper": [Function], "actionNameAttribute": "testID", + "appHangThreshold": undefined, "applicationId": "fake-app-id", "customEndpoint": "https://rum.example.com/", "errorEventMapper": [Function], + "firstPartyHosts": [ + { + "match": "api.com", + "propagatorTypes": [ + "datadog", + "tracecontext", + ], + }, + ], "initialResourceThreshold": 0.123, "longTaskThresholdMs": 567, + "nativeCrashReportEnabled": true, "nativeInteractionTracking": true, + "nativeLongTaskThresholdMs": 345, "nativeViewTracking": true, "resourceEventMapper": [Function], + "resourceTraceSampleRate": 80, "sessionSampleRate": 80, "telemetrySampleRate": 20, "trackBackgroundEvents": true, @@ -192,19 +220,19 @@ describe('DdSdkReactNativeConfiguration', () => { "trackFrustrations": true, "trackInteractions": true, "trackMemoryWarnings": true, + "trackNonFatalAnrs": true, "trackResources": true, "trackWatchdogTerminations": false, + "useAccessibilityLabel": true, "vitalsUpdateFrequency": "AVERAGE", }, "service": "com.test.app", "site": "EU", "traceConfiguration": TraceConfiguration { "customEndpoint": "https://trace.example.com/", - "resourceTraceSampleRate": 80, }, "trackingConsent": "pending", "uploadFrequency": "FREQUENT", - "useAccessibilityLabel": true, "verbosity": "debug", "version": "1.4.5", "versionSuffix": "codepush-3", @@ -217,15 +245,16 @@ describe('DdSdkReactNativeConfiguration', () => { buildConfigurationFromPartialConfiguration( { rumConfiguration: { + useAccessibilityLabel: false, trackErrors: false, trackInteractions: false, - trackResources: false - }, - traceConfiguration: { - resourceTraceSampleRate: 0 + trackResources: false, + resourceTraceSampleRate: 0, + nativeCrashReportEnabled: false, + nativeLongTaskThresholdMs: 0, + actionNameAttribute: '' }, - logsConfiguration: {}, - useAccessibilityLabel: false + logsConfiguration: {} }, { rumConfiguration: { @@ -235,21 +264,25 @@ describe('DdSdkReactNativeConfiguration', () => { nativeInteractionTracking: false, longTaskThresholdMs: false, trackFrustrations: false, - trackBackgroundEvents: false + trackBackgroundEvents: false, + trackMemoryWarnings: false, + trackNonFatalAnrs: false, + telemetrySampleRate: 0, + appHangThreshold: 0, + initialResourceThreshold: 0 }, logsConfiguration: { bundleLogsWithRum: false, bundleLogsWithTraces: false }, + traceConfiguration: {}, clientToken: '', env: '', site: '', service: '', version: '', versionSuffix: '', - additionalConfiguration: {}, - nativeCrashReportEnabled: false, - nativeLongTaskThresholdMs: false + additionalConfiguration: {} } ) ).toMatchInlineSnapshot(` @@ -260,43 +293,52 @@ describe('DdSdkReactNativeConfiguration', () => { "batchSize": "MEDIUM", "clientToken": "", "env": "", - "firstPartyHosts": [], "logsConfiguration": LogsConfiguration { - "bundleLogsWithRum": true, - "bundleLogsWithTraces": true, + "bundleLogsWithRum": false, + "bundleLogsWithTraces": false, + "customEndpoint": undefined, "logEventMapper": null, }, - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, + "actionNameAttribute": "", + "appHangThreshold": 0, "applicationId": "", + "customEndpoint": undefined, "errorEventMapper": null, - "longTaskThresholdMs": 200, + "firstPartyHosts": [], + "initialResourceThreshold": 0, + "longTaskThresholdMs": false, + "nativeCrashReportEnabled": false, "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 0, "nativeViewTracking": false, "resourceEventMapper": null, - "sessionSampleRate": 100, - "telemetrySampleRate": 20, + "resourceTraceSampleRate": 0, + "sessionSampleRate": 0, + "telemetrySampleRate": 0, "trackBackgroundEvents": false, "trackErrors": false, - "trackFrustrations": true, + "trackFrustrations": false, "trackInteractions": false, - "trackMemoryWarnings": true, + "trackMemoryWarnings": false, + "trackNonFatalAnrs": false, "trackResources": false, "trackWatchdogTerminations": false, + "useAccessibilityLabel": false, "vitalsUpdateFrequency": "AVERAGE", }, - "service": undefined, - "site": "US1", + "service": "", + "site": "", "traceConfiguration": TraceConfiguration { - "resourceTraceSampleRate": 0, + "customEndpoint": undefined, }, "trackingConsent": "granted", "uploadFrequency": "AVERAGE", - "useAccessibilityLabel": false, "verbosity": undefined, + "version": "", + "versionSuffix": "", } `); }); diff --git a/packages/core/src/__tests__/InternalLog.test.tsx b/packages/core/src/__tests__/InternalLog.test.tsx index 3269d3205..f6cb65eab 100644 --- a/packages/core/src/__tests__/InternalLog.test.tsx +++ b/packages/core/src/__tests__/InternalLog.test.tsx @@ -5,7 +5,7 @@ */ import { InternalLog } from '../InternalLog'; -import { SdkVerbosity } from '../SdkVerbosity'; +import { SdkVerbosity } from '../config/types'; let baseConsoleDebugCalled = false; let baseConsoleDebugArg; diff --git a/packages/core/src/__tests__/rum/instrumentation/DdEventsInterceptor.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdEventsInterceptor.test.tsx index e49640b46..7d9c24687 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdEventsInterceptor.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdEventsInterceptor.test.tsx @@ -5,8 +5,8 @@ */ import { InternalLog } from '../../../InternalLog'; -import { SdkVerbosity } from '../../../SdkVerbosity'; -import { DdRum } from '../../../index'; +import { SdkVerbosity } from '../../../config/types'; +import { DdRum } from '../../../rum/DdRum'; import { DdEventsInterceptor, UNKNOWN_TARGET_NAME diff --git a/packages/core/src/config/DatadogProviderConfiguration.ts b/packages/core/src/config/DatadogProviderConfiguration.ts new file mode 100644 index 000000000..b8bd020cf --- /dev/null +++ b/packages/core/src/config/DatadogProviderConfiguration.ts @@ -0,0 +1,17 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import { CoreConfiguration } from './features/CoreConfiguration'; +import { InitializationMode } from './types'; + +/** + * SDK Initialization configuration for `DatadogProvider`. + */ +export class DatadogProviderConfiguration extends CoreConfiguration { + /** + * If set to ASYNC, the initialization will be delayed until all animations are completed. + */ + public initializationMode: InitializationMode = InitializationMode.SYNC; +} diff --git a/packages/core/src/config/FileBasedConfiguration.ts b/packages/core/src/config/FileBasedConfiguration.ts new file mode 100644 index 000000000..c226050c0 --- /dev/null +++ b/packages/core/src/config/FileBasedConfiguration.ts @@ -0,0 +1,464 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import type { ActionEventMapper } from '../rum/eventMappers/actionEventMapper'; +import type { ErrorEventMapper } from '../rum/eventMappers/errorEventMapper'; +import type { ResourceEventMapper } from '../rum/eventMappers/resourceEventMapper'; +import type { FirstPartyHost } from '../rum/types'; +import { PropagatorType } from '../rum/types'; + +import { DatadogProviderConfiguration } from './DatadogProviderConfiguration'; +import type { JsonConfiguration } from './FileBasedConfiguration.type'; +import { CORE_DEFAULTS } from './features/CoreConfiguration'; +import { LogsConfiguration } from './features/LogsConfiguration'; +import { RUM_DEFAULTS, RumConfiguration } from './features/RumConfiguration'; +import { TraceConfiguration } from './features/TraceConfiguration'; +import { SdkVerbosity } from './types/SdkVerbosity'; +import { TrackingConsent } from './types/TrackingConsent'; +import { + ProxyConfiguration, + BatchProcessingLevel, + BatchSize, + UploadFrequency, + ProxyType, + VitalsUpdateFrequency +} from './types'; + +/** + * Helper function to remove undefined entries from the given Record + */ +const removeUndefinedEntries = >( + obj: T | undefined +): Partial | undefined => { + if (obj === undefined) { + return undefined; + } + return Object.fromEntries( + Object.entries(obj).filter(([, value]) => value !== undefined) + ) as Partial; +}; + +export class FileBasedConfiguration extends DatadogProviderConfiguration { + constructor(params?: { + configuration?: unknown; + errorEventMapper?: ErrorEventMapper; + resourceEventMapper?: ResourceEventMapper; + actionEventMapper?: ActionEventMapper; + }) { + const jsonConfiguration = getJSONConfiguration(params?.configuration); + + const { + clientToken, + env, + trackingConsent, + ...rest + } = jsonConfiguration; + + if ( + clientToken === undefined || + env === undefined || + trackingConsent === undefined + ) { + console.warn( + 'DATADOG: Warning - Malformed json configuration file - `clientToken`, `env` and `trackingConsent` are mandatory Core SDK properties.' + ); + } + + // Configure Core SDK + const coreConfiguration = removeUndefinedEntries(rest); + super(clientToken, env, trackingConsent, coreConfiguration); + + // Configure RUM + const rumConfig = removeUndefinedEntries( + jsonConfiguration.rumConfiguration + ); + if (rumConfig) { + if (rumConfig.applicationId !== undefined) { + this.rumConfiguration = new RumConfiguration( + rumConfig.applicationId, + rumConfig.trackInteractions, + rumConfig.trackResources, + rumConfig.trackErrors, + rumConfig + ); + + this.rumConfiguration.errorEventMapper = + params?.errorEventMapper ?? RUM_DEFAULTS.errorEventMapper; + this.rumConfiguration.resourceEventMapper = + params?.resourceEventMapper ?? + RUM_DEFAULTS.resourceEventMapper; + this.rumConfiguration.actionEventMapper = + params?.actionEventMapper ?? RUM_DEFAULTS.actionEventMapper; + } else { + console.warn( + 'DATADOG: Warning - Malformed RUM File Configuration - `applicationId` is undefined.' + ); + this.rumConfiguration = undefined; + } + } + + // Configure Logs + const logsConfig = removeUndefinedEntries( + jsonConfiguration.logsConfiguration + ); + if (logsConfig) { + this.logsConfiguration = new LogsConfiguration(logsConfig); + } + + // Configure Trace + const traceConfig = removeUndefinedEntries( + jsonConfiguration.traceConfiguration + ); + if (traceConfig) { + this.traceConfiguration = new TraceConfiguration(traceConfig); + } + } +} + +const resolveJSONConfiguration = ( + userSpecifiedConfiguration: unknown +): Record => { + if (typeof userSpecifiedConfiguration !== 'object') { + console.error(`Failed to parse the Datadog configuration file you provided. +Your configuration must validate the node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json JSON schema. +You can use VSCode to check your configuration by adding the following line to your JSON file: +{ + "$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json", +}`); + + return {}; + } + + return (userSpecifiedConfiguration as any) as Record; +}; + +export const getJSONConfiguration = ( + userSpecifiedConfiguration: unknown +): JsonConfiguration => { + const configuration = resolveJSONConfiguration(userSpecifiedConfiguration); + + return { + additionalConfiguration: configuration.additionalConfiguration, + clientToken: configuration.clientToken, + env: configuration.env, + trackingConsent: buildTrackingConsent(configuration.trackingConsent), + verbosity: buildSdkVerbosity(configuration.verbosity), + site: configuration.site, + service: configuration.service, + version: configuration.version, + versionSuffix: configuration.versionSuffix, + batchSize: buildBatchSize(configuration.batchSize), + batchProcessingLevel: buildBatchProcessingLevel( + configuration.batchProcessingLevel + ), + uploadFrequency: buildUploadFrequency(configuration.uploadFrequency), + proxyConfiguration: buildProxyConfiguration( + configuration.proxyConfiguration + ), + ...(configuration.rumConfiguration !== undefined && { + rumConfiguration: { + applicationId: configuration.rumConfiguration.applicationId, + firstPartyHosts: + buildFirstPartyHosts( + configuration.rumConfiguration.firstPartyHosts + ) || RUM_DEFAULTS.getFirstPartyHosts(), + useAccessibilityLabel: + configuration.rumConfiguration.useAccessibilityLabel, + trackInteractions: + configuration.rumConfiguration.trackInteractions, + trackResources: configuration.rumConfiguration.trackResources, + trackErrors: configuration.rumConfiguration.trackErrors, + nativeLongTaskThresholdMs: + configuration.rumConfiguration.nativeLongTaskThresholdMs, + nativeCrashReportEnabled: + configuration.rumConfiguration.nativeCrashReportEnabled, + longTaskThresholdMs: + configuration.rumConfiguration.longTaskThresholdMs, + actionNameAttribute: + configuration.rumConfiguration.actionNameAttribute, + customEndpoint: configuration.rumConfiguration.customEndpoint, + sessionSampleRate: + configuration.rumConfiguration.sessionSampleRate, + resourceTraceSampleRate: + configuration.rumConfiguration.resourceTraceSampleRate, + trackBackgroundEvents: + configuration.rumConfiguration.trackBackgroundEvents, + trackFrustrations: + configuration.rumConfiguration.trackFrustrations, + trackNonFatalAnrs: + configuration.rumConfiguration.trackNonFatalAnrs, + trackWatchdogTerminations: + configuration.rumConfiguration.trackWatchdogTerminations, + vitalsUpdateFrequency: buildVitalsUpdateFrequency( + configuration.rumConfiguration.vitalsUpdateFrequency + ), + appHangThreshold: + configuration.rumConfiguration.appHangThreshold, + initialResourceThreshold: + configuration.rumConfiguration.initialResourceThreshold, + nativeViewTracking: + configuration.rumConfiguration.nativeViewTracking, + trackMemoryWarnings: + configuration.rumConfiguration.trackMemoryWarnings, + telemetrySampleRate: + configuration.rumConfiguration.telemetrySampleRate, + nativeInteractionTracking: + configuration.rumConfiguration.nativeInteractionTracking + } + }), + ...(configuration.traceConfiguration !== undefined && { + traceConfiguration: { + customEndpoint: configuration.traceConfiguration.customEndpoint + } + }), + ...(configuration.logsConfiguration !== undefined && { + logsConfiguration: { + bundleLogsWithRum: + configuration.logsConfiguration.bundleLogsWithRum, + bundleLogsWithTraces: + configuration.logsConfiguration.bundleLogsWithTraces, + customEndpoint: configuration.logsConfiguration.customEndpoint + } + }) + }; +}; + +const buildFirstPartyHosts = ( + firstPartyHosts: { match: string; propagatorTypes: string[] }[] | undefined +): FirstPartyHost[] | undefined => { + if (!firstPartyHosts) { + return undefined; + } + + try { + return firstPartyHosts.map(({ match, propagatorTypes }) => ({ + match, + propagatorTypes: propagatorTypes.map(formatPropagatorType) + })); + } catch (error) { + console.error(`Failed to parse the first party hosts from the Datadog configuration file you provided: +${(error as any).message} +The first party hosts will not be set for this session. +`); + return undefined; + } +}; + +export const formatPropagatorType = ( + propagatorType: string +): PropagatorType => { + switch (propagatorType.toLowerCase()) { + case 'b3': { + return PropagatorType.B3; + } + case 'b3multi': { + return PropagatorType.B3MULTI; + } + case 'datadog': { + return PropagatorType.DATADOG; + } + case 'tracecontext': { + return PropagatorType.TRACECONTEXT; + } + default: { + throw new Error( + `Failed to parse propagator type ${propagatorType}.` + ); + } + } +}; + +const buildTrackingConsent = ( + trackingConsent: string | undefined +): TrackingConsent => { + if (trackingConsent === undefined) { + return CORE_DEFAULTS.trackingConsent; + } + + switch (trackingConsent.toLowerCase()) { + case 'granted': { + return TrackingConsent.GRANTED; + } + case 'pending': { + return TrackingConsent.PENDING; + } + case 'not_granted': { + return TrackingConsent.NOT_GRANTED; + } + default: { + return CORE_DEFAULTS.trackingConsent; + } + } +}; + +const buildSdkVerbosity = ( + verbosity: string | undefined +): SdkVerbosity | undefined => { + if (verbosity === undefined) { + return CORE_DEFAULTS.verbosity; + } + switch (verbosity.toLowerCase()) { + case 'debug': { + return SdkVerbosity.DEBUG; + } + case 'info': { + return SdkVerbosity.INFO; + } + case 'warn': { + return SdkVerbosity.WARN; + } + case 'error': { + return SdkVerbosity.ERROR; + } + default: { + return undefined; + } + } +}; + +const buildBatchSize = (batchSize: string | undefined): BatchSize => { + if (batchSize === undefined) { + return CORE_DEFAULTS.batchSize; + } + + switch (batchSize.toLowerCase()) { + case 'large': + return BatchSize.LARGE; + case 'medium': + return BatchSize.MEDIUM; + case 'small': + return BatchSize.SMALL; + default: + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid batchSize: ${batchSize}. The default value will be used: ${CORE_DEFAULTS.batchSize}.` + ); + return CORE_DEFAULTS.batchSize; + } +}; + +const buildBatchProcessingLevel = ( + batchProcessingLevel: string | undefined +): BatchProcessingLevel => { + if (batchProcessingLevel === undefined) { + return CORE_DEFAULTS.batchProcessingLevel; + } + + switch (batchProcessingLevel.toLowerCase()) { + case 'high': + return BatchProcessingLevel.HIGH; + case 'medium': + return BatchProcessingLevel.MEDIUM; + case 'low': + return BatchProcessingLevel.LOW; + default: + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid batchProcessingLevel: ${batchProcessingLevel}. The default value will be used: ${CORE_DEFAULTS.batchProcessingLevel}.` + ); + return CORE_DEFAULTS.batchProcessingLevel; + } +}; + +const buildUploadFrequency = ( + uploadFrequency: string | undefined +): UploadFrequency => { + if (uploadFrequency === undefined) { + return CORE_DEFAULTS.uploadFrequency; + } + + switch (uploadFrequency.toLowerCase()) { + case 'frequent': + return UploadFrequency.FREQUENT; + case 'average': + return UploadFrequency.AVERAGE; + case 'rare': + return UploadFrequency.RARE; + default: + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid uploadFrequency: ${uploadFrequency}. The default value will be used: ${CORE_DEFAULTS.uploadFrequency}.` + ); + return CORE_DEFAULTS.uploadFrequency; + } +}; + +const proxyConfigurationToString = (proxyConfiguration: object | undefined) => { + if (proxyConfiguration === undefined) { + return 'undefined'; + } + try { + return JSON.stringify(proxyConfiguration, null, 2); + } catch (ignored) { + return String(proxyConfiguration); + } +}; + +const buildProxyType = (proxyType: string): ProxyType | undefined => { + switch (proxyType.toLowerCase()) { + case 'http': + return ProxyType.HTTP; + case 'https': + return ProxyType.HTTPS; + case 'socks': + return ProxyType.SOCKS; + default: + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid proxy type '${proxyType}' for proxyConfiguration.` + ); + return undefined; + } +}; + +const buildProxyConfiguration = ( + proxyConfiguration: object +): ProxyConfiguration | undefined => { + if (proxyConfiguration === undefined) { + return CORE_DEFAULTS.proxyConfiguration; + } + + const { + rawType, + address, + port, + username, + password + } = proxyConfiguration as any; + const type = buildProxyType(rawType); + if (type === undefined || address === undefined || port === undefined) { + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid proxyConfiguration: ${proxyConfigurationToString( + proxyConfiguration + )}. The default value will be used: ${ + CORE_DEFAULTS.proxyConfiguration + }.` + ); + return CORE_DEFAULTS.proxyConfiguration; + } + + return new ProxyConfiguration(type, address, port, username, password); +}; + +const buildVitalsUpdateFrequency = ( + vitalsUpdateFrequency: string | undefined +): VitalsUpdateFrequency | undefined => { + if (vitalsUpdateFrequency === undefined) { + return RUM_DEFAULTS.vitalsUpdateFrequency; + } + + switch (vitalsUpdateFrequency.toLowerCase()) { + case 'frequent': + return VitalsUpdateFrequency.FREQUENT; + case 'average': + return VitalsUpdateFrequency.AVERAGE; + case 'rare': + return VitalsUpdateFrequency.RARE; + case 'never': + return VitalsUpdateFrequency.NEVER; + default: + console.warn( + `DATADOG: Warning - Malformed json configuration file - invalid vitalsUpdateFrequency: ${vitalsUpdateFrequency} for rumConfiguration. The default value will be used: ${RUM_DEFAULTS.vitalsUpdateFrequency}` + ); + return RUM_DEFAULTS.vitalsUpdateFrequency; + } +}; diff --git a/packages/core/src/config/FileBasedConfiguration.type.ts b/packages/core/src/config/FileBasedConfiguration.type.ts new file mode 100644 index 000000000..693fa5738 --- /dev/null +++ b/packages/core/src/config/FileBasedConfiguration.type.ts @@ -0,0 +1,66 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { FirstPartyHost } from '../rum/types'; + +import type { CoreConfigurationOptions } from './features/CoreConfiguration.type'; +import type { + BatchProcessingLevel, + BatchSize, + ProxyConfiguration, + SdkVerbosity, + TrackingConsent, + UploadFrequency, + VitalsUpdateFrequency +} from './types'; + +export interface JsonConfiguration extends CoreConfigurationOptions { + clientToken: string; + env: string; + trackingConsent?: TrackingConsent; + verbosity?: SdkVerbosity; + service?: string; + site?: string; + batchSize?: BatchSize; + batchProcessingLevel?: BatchProcessingLevel; + proxyConfiguration?: ProxyConfiguration; + uploadFrequency?: UploadFrequency; + version?: string; + versionSuffix?: string; + rumConfiguration?: { + applicationId: string; + useAccessibilityLabel?: boolean; + trackInteractions?: boolean; + trackResources?: boolean; + trackErrors?: boolean; + longTaskThresholdMs?: number; + actionNameAttribute?: string; + appHangThreshold?: number; + firstPartyHosts?: FirstPartyHost[]; + initialResourceThreshold?: number; + trackMemoryWarnings?: boolean; + nativeCrashReportEnabled?: boolean; + nativeLongTaskThresholdMs?: number; + nativeViewTracking?: boolean; + nativeInteractionTracking?: boolean; + customEndpoint?: string; + sessionSampleRate?: number; + resourceTraceSampleRate?: number; + trackBackgroundEvents?: boolean; + trackFrustrations?: boolean; + trackNonFatalAnrs?: boolean; + trackWatchdogTerminations?: boolean; + vitalsUpdateFrequency?: VitalsUpdateFrequency; + telemetrySampleRate?: number; + }; + traceConfiguration?: { + customEndpoint?: string; + }; + logsConfiguration?: { + bundleLogsWithRum?: boolean; + bundleLogsWithTraces?: boolean; + customEndpoint?: string; + }; +} diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/config/__tests__/FileBasedConfiguration.test.ts similarity index 64% rename from packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts rename to packages/core/src/config/__tests__/FileBasedConfiguration.test.ts index 47826f030..5b062447f 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/config/__tests__/FileBasedConfiguration.test.ts @@ -4,11 +4,10 @@ * Copyright 2016-Present Datadog, Inc. */ -import { PropagatorType } from '../../../rum/types'; +import { PropagatorType } from '../../rum/types'; import { FileBasedConfiguration, - formatPropagatorType, - getJSONConfiguration + formatPropagatorType } from '../FileBasedConfiguration'; import configurationAllFields from './__fixtures__/configuration-all-fields.json'; @@ -23,103 +22,120 @@ describe('FileBasedConfiguration', () => { expect(configuration).toMatchInlineSnapshot(` FileBasedConfiguration { - "additionalConfiguration": {}, + "additionalConfiguration": { + "another.property": "test", + "myProperty": 1, + }, "attributeEncoders": [], - "batchProcessingLevel": "MEDIUM", - "batchSize": "MEDIUM", + "batchProcessingLevel": "LOW", + "batchSize": "SMALL", "clientToken": "fake-client-token", "env": "fake-env", - "firstPartyHosts": [ - { - "match": "example.com", - "propagatorTypes": [ - "b3multi", - "tracecontext", - ], - }, - ], "initializationMode": "SYNC", "logsConfiguration": LogsConfiguration { "bundleLogsWithRum": true, "bundleLogsWithTraces": true, + "customEndpoint": "https://example.com/logs", "logEventMapper": null, }, - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, "actionNameAttribute": "action-name-attr", + "appHangThreshold": 123, "applicationId": "fake-app-id", + "customEndpoint": "https://example.com/rum", "errorEventMapper": null, + "firstPartyHosts": [ + { + "match": "example.com", + "propagatorTypes": [ + "b3multi", + "tracecontext", + ], + }, + ], + "initialResourceThreshold": 456, "longTaskThresholdMs": 44, - "nativeInteractionTracking": false, - "nativeViewTracking": false, + "nativeCrashReportEnabled": true, + "nativeInteractionTracking": true, + "nativeLongTaskThresholdMs": 789, + "nativeViewTracking": true, "resourceEventMapper": null, + "resourceTraceSampleRate": 33, "sessionSampleRate": 100, "telemetrySampleRate": 20, - "trackBackgroundEvents": false, + "trackBackgroundEvents": true, "trackErrors": true, "trackFrustrations": true, "trackInteractions": true, - "trackMemoryWarnings": true, + "trackMemoryWarnings": false, + "trackNonFatalAnrs": true, "trackResources": true, - "trackWatchdogTerminations": false, - "vitalsUpdateFrequency": "AVERAGE", + "trackWatchdogTerminations": true, + "useAccessibilityLabel": false, + "vitalsUpdateFrequency": "NEVER", }, - "service": undefined, + "service": "my-custom-service", "site": "US5", "traceConfiguration": TraceConfiguration { - "resourceTraceSampleRate": 33, + "customEndpoint": "https://example.com/trace", }, "trackingConsent": "not_granted", - "uploadFrequency": "AVERAGE", - "useAccessibilityLabel": false, + "uploadFrequency": "RARE", "verbosity": "warn", + "version": "1.2.3", + "versionSuffix": "test-suffix", } `); }); it('prints a warning message when the configuration file cannot be parsed correctly', () => { const warnSpy = jest.spyOn(console, 'warn'); - getJSONConfiguration(malformedConfiguration); + const config = new FileBasedConfiguration({ + configuration: malformedConfiguration + }); + expect(config).not.toBeUndefined(); + expect(warnSpy).toHaveBeenCalledTimes(2); + expect(warnSpy).toHaveBeenCalledWith( + 'DATADOG: Warning - Malformed json configuration file - `clientToken`, `env` and `trackingConsent` are mandatory Core SDK properties.' + ); expect(warnSpy).toHaveBeenCalledWith( - 'DATADOG: Warning: Malformed json configuration file - clientToken and env are mandatory Core SDK properties. ApplicationId is mandatory to enable RUM.' + 'DATADOG: Warning - Malformed RUM File Configuration - `applicationId` is undefined.' ); }); - it('resolves all properties from a given file path', () => { + it('resolves all properties from a given configuration', () => { const config = new FileBasedConfiguration({ configuration: { rumConfiguration: { + useAccessibilityLabel: false, trackInteractions: true, trackResources: true, trackErrors: true, applicationId: 'fake-app-id', longTaskThresholdMs: 44, - actionNameAttribute: 'action-name-attr' + actionNameAttribute: 'action-name-attr', + resourceTraceSampleRate: 33, + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: [ + 'B3MULTI', + 'TRACECONTEXT', + 'B3', + 'DATADOG' + ] + } + ] }, env: 'fake-env', clientToken: 'fake-client-token', trackingConsent: 'NOT_GRANTED', site: 'US5', verbosity: 'WARN', - useAccessibilityLabel: false, - traceConfiguration: { - resourceTraceSampleRate: 33 - }, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [ - 'B3MULTI', - 'TRACECONTEXT', - 'B3', - 'DATADOG' - ] - } - ] + traceConfiguration: {} } }); expect(config).toMatchInlineSnapshot(` @@ -130,30 +146,35 @@ describe('FileBasedConfiguration', () => { "batchSize": "MEDIUM", "clientToken": "fake-client-token", "env": "fake-env", - "firstPartyHosts": [ - { - "match": "example.com", - "propagatorTypes": [ - "b3multi", - "tracecontext", - "b3", - "datadog", - ], - }, - ], "initializationMode": "SYNC", - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, + "logsConfiguration": undefined, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, "actionNameAttribute": "action-name-attr", + "appHangThreshold": undefined, "applicationId": "fake-app-id", + "customEndpoint": undefined, "errorEventMapper": null, + "firstPartyHosts": [ + { + "match": "example.com", + "propagatorTypes": [ + "b3multi", + "tracecontext", + "b3", + "datadog", + ], + }, + ], + "initialResourceThreshold": undefined, "longTaskThresholdMs": 44, + "nativeCrashReportEnabled": false, "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 200, "nativeViewTracking": false, "resourceEventMapper": null, + "resourceTraceSampleRate": 33, "sessionSampleRate": 100, "telemetrySampleRate": 20, "trackBackgroundEvents": false, @@ -161,23 +182,27 @@ describe('FileBasedConfiguration', () => { "trackFrustrations": true, "trackInteractions": true, "trackMemoryWarnings": true, + "trackNonFatalAnrs": undefined, "trackResources": true, "trackWatchdogTerminations": false, + "useAccessibilityLabel": false, "vitalsUpdateFrequency": "AVERAGE", }, "service": undefined, "site": "US5", "traceConfiguration": TraceConfiguration { - "resourceTraceSampleRate": 33, + "customEndpoint": undefined, }, "trackingConsent": "not_granted", "uploadFrequency": "AVERAGE", - "useAccessibilityLabel": false, "verbosity": "warn", + "version": undefined, + "versionSuffix": undefined, } `); }); - it('applies default values to configuration from a given file path', () => { + + it('applies default values when parsing minimal configuration', () => { const config = new FileBasedConfiguration({ configuration: { env: 'fake-env', @@ -195,19 +220,25 @@ describe('FileBasedConfiguration', () => { "batchSize": "MEDIUM", "clientToken": "fake-client-token", "env": "fake-env", - "firstPartyHosts": [], "initializationMode": "SYNC", - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, + "logsConfiguration": undefined, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, + "actionNameAttribute": undefined, + "appHangThreshold": undefined, "applicationId": "fake-app-id", + "customEndpoint": undefined, "errorEventMapper": null, + "firstPartyHosts": [], + "initialResourceThreshold": undefined, "longTaskThresholdMs": 0, + "nativeCrashReportEnabled": false, "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 200, "nativeViewTracking": false, "resourceEventMapper": null, + "resourceTraceSampleRate": 100, "sessionSampleRate": 100, "telemetrySampleRate": 20, "trackBackgroundEvents": false, @@ -215,19 +246,24 @@ describe('FileBasedConfiguration', () => { "trackFrustrations": true, "trackInteractions": false, "trackMemoryWarnings": true, + "trackNonFatalAnrs": undefined, "trackResources": false, "trackWatchdogTerminations": false, + "useAccessibilityLabel": true, "vitalsUpdateFrequency": "AVERAGE", }, "service": undefined, "site": "US1", + "traceConfiguration": undefined, "trackingConsent": "granted", "uploadFrequency": "AVERAGE", - "useAccessibilityLabel": true, "verbosity": undefined, + "version": undefined, + "versionSuffix": undefined, } `); }); + it('applies event mappers to configuration when provided', () => { const actionEventMapper = () => null; const errorEventMapper = () => null; @@ -254,6 +290,7 @@ describe('FileBasedConfiguration', () => { resourceEventMapper ); }); + it('prints a warning message when the configuration file cannot be parsed correctly', () => { expect( () => @@ -266,21 +303,24 @@ describe('FileBasedConfiguration', () => { }) ).not.toThrow(); }); + it('prints a warning message when the first party hosts contain unknown propagator types', () => { const config = new FileBasedConfiguration({ configuration: { - applicationId: 'fake-app-id', env: 'fake-env', clientToken: 'fake-client-token', - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: ['UNKNOWN'] - } - ] + rumConfiguration: { + applicationId: 'fake-app-id', + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: ['UNKNOWN'] + } + ] + } } }); - expect(config.firstPartyHosts).toHaveLength(0); + expect(config.rumConfiguration?.firstPartyHosts).toHaveLength(0); }); }); diff --git a/packages/core/src/config/__tests__/__fixtures__/configuration-all-fields.json b/packages/core/src/config/__tests__/__fixtures__/configuration-all-fields.json new file mode 100644 index 000000000..3b5d15d59 --- /dev/null +++ b/packages/core/src/config/__tests__/__fixtures__/configuration-all-fields.json @@ -0,0 +1,57 @@ +{ + "$schema": "../../../../../datadog-configuration.schema.json", + "configuration": { + "env": "fake-env", + "clientToken": "fake-client-token", + "rumConfiguration": { + "appHangThreshold": 123, + "applicationId": "fake-app-id", + "customEndpoint": "https://example.com/rum", + "useAccessibilityLabel": false, + "trackInteractions": true, + "trackResources": true, + "trackErrors": true, + "longTaskThresholdMs": 44, + "trackNonFatalAnrs": true, + "actionNameAttribute": "action-name-attr", + "resourceTraceSampleRate": 33, + "firstPartyHosts": [ + { + "match": "example.com", + "propagatorTypes": [ + "B3MULTI", + "TRACECONTEXT" + ] + } + ], + "initialResourceThreshold": 456, + "nativeViewTracking": true, + "nativeInteractionTracking": true, + "nativeLongTaskThresholdMs": 789, + "nativeCrashReportEnabled": true, + "trackBackgroundEvents": true, + "trackMemoryWarnings": false, + "trackWatchdogTerminations": true, + "vitalsUpdateFrequency": "NEVER" + }, + "traceConfiguration": { + "customEndpoint": "https://example.com/trace" + }, + "logsConfiguration": { + "customEndpoint": "https://example.com/logs" + }, + "trackingConsent": "NOT_GRANTED", + "verbosity": "WARN", + "site": "US5", + "service": "my-custom-service", + "version": "1.2.3", + "versionSuffix": "test-suffix", + "additionalConfiguration": { + "myProperty": 1, + "another.property": "test" + }, + "batchSize": "SMALL", + "uploadFrequency": "RARE", + "batchProcessingLevel": "LOW" + } +} diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json b/packages/core/src/config/__tests__/__fixtures__/malformed-configuration.json similarity index 50% rename from packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json rename to packages/core/src/config/__tests__/__fixtures__/malformed-configuration.json index 0e1b26639..732ee5424 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json +++ b/packages/core/src/config/__tests__/__fixtures__/malformed-configuration.json @@ -1,4 +1,4 @@ { "clientToken": "clientToken", - "applicationId": "applicationId" + "rumConfiguration": {} } diff --git a/packages/core/src/config/async/AutoInstrumentationConfiguration.ts b/packages/core/src/config/async/AutoInstrumentationConfiguration.ts new file mode 100644 index 000000000..199aaa4eb --- /dev/null +++ b/packages/core/src/config/async/AutoInstrumentationConfiguration.ts @@ -0,0 +1,126 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { ActionEventMapper } from '../../rum/eventMappers/actionEventMapper'; +import type { ErrorEventMapper } from '../../rum/eventMappers/errorEventMapper'; +import type { ResourceEventMapper } from '../../rum/eventMappers/resourceEventMapper'; +import type { FirstPartyHost } from '../../rum/types'; +import type { LogEventMapper } from '../../types'; +import { LOGS_DEFAULTS } from '../features/LogsConfiguration'; +import { RUM_DEFAULTS } from '../features/RumConfiguration'; +import type { TraceConfiguration } from '../features/TraceConfiguration'; + +/** + * Auto Instrumentation configuration passed to DatadogProvider. + * Does not include default values. + */ +export type AutoInstrumentationConfiguration = { + readonly rumConfiguration: { + readonly trackInteractions: boolean; + readonly trackResources: boolean; + readonly trackErrors: boolean; + readonly useAccessibilityLabel?: boolean; + readonly actionNameAttribute?: string; + readonly resourceTraceSampleRate?: number; + readonly nativeCrashReportEnabled?: boolean; + readonly nativeLongTaskThresholdMs?: number; + readonly nativeViewTracking?: boolean; + readonly actionEventMapper?: ActionEventMapper | null; + readonly errorEventMapper?: ErrorEventMapper | null; + readonly resourceEventMapper?: ResourceEventMapper | null; + readonly firstPartyHosts?: FirstPartyHost[]; + }; + readonly logsConfiguration: { + readonly logEventMapper?: LogEventMapper | null; + }; +}; + +/** + * Parameters needed to start auto instrumentation. Includes default values. + */ +export type AutoInstrumentationParameters = { + readonly rumConfiguration?: { + readonly useAccessibilityLabel: boolean; + readonly trackInteractions: boolean; + readonly trackResources: boolean; + readonly trackErrors: boolean; + readonly actionNameAttribute?: string; + readonly resourceTraceSampleRate?: number; + readonly nativeCrashReportEnabled?: boolean; + readonly nativeLongTaskThresholdMs?: number; + readonly nativeViewTracking?: boolean; + readonly actionEventMapper: ActionEventMapper | null; + readonly errorEventMapper: ErrorEventMapper | null; + readonly resourceEventMapper: ResourceEventMapper | null; + readonly firstPartyHosts: FirstPartyHost[]; + }; + readonly logsConfiguration?: { + readonly logEventMapper: LogEventMapper | null; + }; + readonly traceConfiguration?: TraceConfiguration; +}; + +/** + * We could use `Proxy` instead of this function, but `Proxy` is not available on + * the older android jsc that can still be used. + */ +export const addDefaultValuesToAutoInstrumentationConfiguration = ( + features: AutoInstrumentationConfiguration +): AutoInstrumentationParameters => { + return { + rumConfiguration: { + useAccessibilityLabel: + features.rumConfiguration.useAccessibilityLabel === undefined + ? RUM_DEFAULTS.useAccessibilityLabel + : features.rumConfiguration.useAccessibilityLabel, + trackInteractions: + features.rumConfiguration.trackInteractions ?? + RUM_DEFAULTS.trackInteractions, + trackResources: + features.rumConfiguration.trackResources ?? + RUM_DEFAULTS.trackResources, + trackErrors: + features.rumConfiguration.trackErrors ?? + RUM_DEFAULTS.trackErrors, + actionNameAttribute: + features.rumConfiguration.actionNameAttribute ?? + RUM_DEFAULTS.actionNameAttribute, + errorEventMapper: + features.rumConfiguration.errorEventMapper === undefined + ? RUM_DEFAULTS.errorEventMapper + : features.rumConfiguration.errorEventMapper, + resourceEventMapper: + features.rumConfiguration.resourceEventMapper === undefined + ? RUM_DEFAULTS.resourceEventMapper + : features.rumConfiguration.resourceEventMapper, + actionEventMapper: + features.rumConfiguration.actionEventMapper === undefined + ? RUM_DEFAULTS.actionEventMapper + : features.rumConfiguration.actionEventMapper, + resourceTraceSampleRate: + features.rumConfiguration.resourceTraceSampleRate === undefined + ? RUM_DEFAULTS.resourceTraceSampleRate + : features.rumConfiguration.resourceTraceSampleRate, + nativeCrashReportEnabled: + features.rumConfiguration.nativeCrashReportEnabled ?? + RUM_DEFAULTS.nativeCrashReportEnabled, + nativeLongTaskThresholdMs: + features.rumConfiguration.nativeLongTaskThresholdMs ?? + RUM_DEFAULTS.nativeLongTaskThresholdMs, + nativeViewTracking: + features.rumConfiguration.nativeViewTracking ?? + RUM_DEFAULTS.nativeViewTracking, + firstPartyHosts: + features.rumConfiguration.firstPartyHosts || + RUM_DEFAULTS.getFirstPartyHosts() + }, + logsConfiguration: { + logEventMapper: + features.logsConfiguration.logEventMapper === undefined + ? LOGS_DEFAULTS.logEventMapper + : features.logsConfiguration.logEventMapper + } + }; +}; diff --git a/packages/core/src/config/async/PartialInitializationConfiguration.ts b/packages/core/src/config/async/PartialInitializationConfiguration.ts new file mode 100644 index 000000000..3e3a7b522 --- /dev/null +++ b/packages/core/src/config/async/PartialInitializationConfiguration.ts @@ -0,0 +1,58 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { AttributeEncoder } from '../../sdk/AttributesEncoding/types'; +import type { + BatchProcessingLevel, + BatchSize, + ProxyConfiguration, + SdkVerbosity, + TrackingConsent, + UploadFrequency, + VitalsUpdateFrequency +} from '../types'; + +export type PartialInitializationConfiguration = { + readonly additionalConfiguration?: { [k: string]: any }; + readonly attributeEncoders?: AttributeEncoder[]; + readonly clientToken: string; + readonly env: string; + readonly site?: string; + readonly trackingConsent?: TrackingConsent; + readonly verbosity?: SdkVerbosity | undefined; + readonly service?: string; + readonly version?: string; + versionSuffix?: string; + readonly proxyConfiguration?: ProxyConfiguration; + readonly uploadFrequency?: UploadFrequency; + readonly batchSize?: BatchSize; + readonly batchProcessingLevel?: BatchProcessingLevel; + readonly rumConfiguration?: { + readonly applicationId: string; + readonly sessionSampleRate?: number; + readonly nativeLongTaskThresholdMs?: number; + readonly nativeCrashReportEnabled?: boolean; + readonly nativeViewTracking?: boolean; + readonly nativeInteractionTracking?: boolean; + readonly longTaskThresholdMs?: number | false; + readonly vitalsUpdateFrequency?: VitalsUpdateFrequency; + readonly trackFrustrations?: boolean; + readonly trackBackgroundEvents?: boolean; + readonly initialResourceThreshold?: number; + readonly trackMemoryWarnings?: boolean; + readonly telemetrySampleRate?: number; + readonly customEndpoint?: string; + readonly trackNonFatalAnrs?: boolean; + readonly appHangThreshold?: number; + }; + readonly logsConfiguration?: { + readonly bundleLogsWithRum?: boolean; + readonly bundleLogsWithTraces?: boolean; + readonly customEndpoint?: string; + }; + readonly traceConfiguration?: { + readonly customEndpoint?: string; + }; +}; diff --git a/packages/core/src/config/async/asyncInitializationHelper.tsx b/packages/core/src/config/async/asyncInitializationHelper.tsx new file mode 100644 index 000000000..eec1624fd --- /dev/null +++ b/packages/core/src/config/async/asyncInitializationHelper.tsx @@ -0,0 +1,51 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import { CoreConfiguration } from '../features/CoreConfiguration'; +import { LogsConfiguration } from '../features/LogsConfiguration'; +import { RumConfiguration } from '../features/RumConfiguration'; + +import type { AutoInstrumentationConfiguration } from './AutoInstrumentationConfiguration'; +import type { PartialInitializationConfiguration } from './PartialInitializationConfiguration'; + +export const buildConfigurationFromPartialConfiguration = ( + features: AutoInstrumentationConfiguration, + configuration: PartialInitializationConfiguration +): CoreConfiguration => { + const { clientToken, env } = configuration; + + const coreConfiguration = new CoreConfiguration( + clientToken, + env, + configuration.trackingConsent, + configuration + ); + + // RUM + if (configuration.rumConfiguration?.applicationId !== undefined) { + coreConfiguration.rumConfiguration = new RumConfiguration( + configuration.rumConfiguration.applicationId, + features.rumConfiguration.trackInteractions, + features.rumConfiguration.trackResources, + features.rumConfiguration.trackErrors, + { ...features.rumConfiguration, ...configuration.rumConfiguration } + ); + } else { + console.warn( + 'DATADOG: Warning - Malformed RUM Configuration - `applicationId` is undefined' + ); + coreConfiguration.rumConfiguration = undefined; + } + + // Logs + if (features.logsConfiguration.logEventMapper) { + coreConfiguration.logsConfiguration = new LogsConfiguration({ + ...configuration.logsConfiguration, + logEventMapper: features.logsConfiguration.logEventMapper + }); + } + + return coreConfiguration; +}; diff --git a/packages/core/src/config/features/CoreConfiguration.ts b/packages/core/src/config/features/CoreConfiguration.ts new file mode 100644 index 000000000..53642c7e5 --- /dev/null +++ b/packages/core/src/config/features/CoreConfiguration.ts @@ -0,0 +1,130 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { AttributeEncoder } from '../../sdk/AttributesEncoding/types'; +import type { SdkVerbosity, ProxyConfiguration } from '../types'; +import { + TrackingConsent, + BatchProcessingLevel, + BatchSize, + UploadFrequency +} from '../types'; + +import type { + CoreConfigurationOptions, + CoreConfigurationType +} from './CoreConfiguration.type'; +import { LogsConfiguration } from './LogsConfiguration'; +import { RumConfiguration } from './RumConfiguration'; +import { TraceConfiguration } from './TraceConfiguration'; + +const DEFAULTS = { + getAdditionalConfiguration: () => ({}), + attributeEncoders: [] as AttributeEncoder[], + batchProcessingLevel: BatchProcessingLevel.MEDIUM, + batchSize: BatchSize.MEDIUM, + logsConfiguration: undefined, + proxyConfiguration: undefined, + rumConfiguration: undefined, + service: undefined, + site: 'US1', + traceConfiguration: undefined, + uploadFrequency: UploadFrequency.AVERAGE, + verbosity: undefined, + version: undefined, + versionSuffix: undefined, + trackingConsent: TrackingConsent.GRANTED +}; + +/** + * The Core configuration class. + * Used to configure the SDK functionality at initialization. + */ +class CoreConfiguration implements CoreConfigurationType { + // Additional Configuration + public additionalConfiguration?: { + [k: string]: any; + } = DEFAULTS.getAdditionalConfiguration(); + + // Attribute Encoders + public attributeEncoders: AttributeEncoder[] = + DEFAULTS.attributeEncoders; + + // Batch Processing Level + public batchProcessingLevel: BatchProcessingLevel = + DEFAULTS.batchProcessingLevel; + + // Batch Size + public batchSize: BatchSize = DEFAULTS.batchSize; + + // Logs Configuration + public logsConfiguration?: LogsConfiguration = DEFAULTS.logsConfiguration; + + // Proxy Configuration + public proxyConfiguration?: ProxyConfiguration = + DEFAULTS.proxyConfiguration; + + // RUM Configuration + public rumConfiguration?: RumConfiguration = DEFAULTS.rumConfiguration; + + // Service Name + public service?: string = DEFAULTS.service; + + // Datadog Site + public site: string = DEFAULTS.site; + + // Trace Configuration + public traceConfiguration?: TraceConfiguration = + DEFAULTS.traceConfiguration; + + // Batch Upload Frequency + public uploadFrequency: UploadFrequency = DEFAULTS.uploadFrequency; + + // SDK Logs Verbosity + public verbosity?: SdkVerbosity = DEFAULTS.verbosity; + + // Custom Version Name + public version?: string = DEFAULTS.version; + + // Custom Version Suffix + public versionSuffix?: string = DEFAULTS.versionSuffix; + + constructor( + readonly clientToken: string, + readonly env: string, + readonly trackingConsent: TrackingConsent = DEFAULTS.trackingConsent, + options: CoreConfigurationOptions = {} + ) { + const { + rumConfiguration, + logsConfiguration, + traceConfiguration, + ...rest + } = options; + + Object.assign(this, rest); + if (rumConfiguration !== undefined) { + this.rumConfiguration = new RumConfiguration( + rumConfiguration.applicationId, + rumConfiguration.trackInteractions, + rumConfiguration.trackResources, + rumConfiguration.trackErrors, + rumConfiguration + ); + } + + if (logsConfiguration !== undefined) { + this.logsConfiguration = new LogsConfiguration(logsConfiguration); + } + + if (traceConfiguration !== undefined) { + this.traceConfiguration = new TraceConfiguration( + traceConfiguration + ); + } + } +} + +export { DEFAULTS as CORE_DEFAULTS, CoreConfiguration }; diff --git a/packages/core/src/config/features/CoreConfiguration.type.ts b/packages/core/src/config/features/CoreConfiguration.type.ts new file mode 100644 index 000000000..c005b5e5b --- /dev/null +++ b/packages/core/src/config/features/CoreConfiguration.type.ts @@ -0,0 +1,119 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { AttributeEncoder } from '../../sdk/AttributesEncoding/types'; +import type { + BatchProcessingLevel, + BatchSize, + ProxyConfiguration, + SdkVerbosity, + TrackingConsent, + UploadFrequency +} from '../types'; + +import type { LogsConfigurationType } from './LogsConfiguration.type'; +import type { RumConfigurationType } from './RumConfiguration.type'; +import type { TraceConfigurationType } from './TraceConfiguration.type'; + +/** + * Required core configuration values. + */ +export interface CoreConfigurationRequired { + /** + * The Datadog client token. + */ + clientToken: string; + + /** + * The environment name (e.g. prod, staging). + */ + env: string; + + /** + * Initial tracking consent value. + */ + trackingConsent: TrackingConsent; +} + +/** + * Optional core configuration values. + */ +export interface CoreConfigurationOptions { + /** + * Additional arbitrary configuration values. + */ + additionalConfiguration?: { [k: string]: any }; + + /** + * Custom attribute encoders. + */ + attributeEncoders?: AttributeEncoder[]; + + /** + * Batch size used when sending data. + */ + batchSize?: BatchSize; + + /** + * Processing level used when batching data. + */ + batchProcessingLevel?: BatchProcessingLevel; + + /** + * Logs feature configuration. + */ + logsConfiguration?: LogsConfigurationType; + + /** + * Proxy configuration. + */ + proxyConfiguration?: ProxyConfiguration; + + /** + * RUM feature configuration. + */ + rumConfiguration?: RumConfigurationType; + + /** + * Name of the service. + */ + service?: string; + + /** + * Datadog site to send data to. + */ + site?: string; + + /** + * Trace feature configuration. + */ + traceConfiguration?: TraceConfigurationType; + + /** + * Frequency used to upload data batches. + */ + uploadFrequency?: UploadFrequency; + + /** + * Overrides the reported application version. + */ + version?: string; + + /** + * Suffix added to the reported application version. + */ + versionSuffix?: string; + + /** + * SDK internal logging verbosity. + */ + verbosity?: SdkVerbosity; +} + +/** + * Complete core configuration type. + */ +export type CoreConfigurationType = CoreConfigurationRequired & + CoreConfigurationOptions; diff --git a/packages/core/src/config/features/CoreConfigurationNative.ts b/packages/core/src/config/features/CoreConfigurationNative.ts new file mode 100644 index 000000000..5bb91f501 --- /dev/null +++ b/packages/core/src/config/features/CoreConfigurationNative.ts @@ -0,0 +1,50 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { AttributeEncoder } from '../../sdk/AttributesEncoding/types'; +import type { BatchProcessingLevel } from '../types'; + +import type { LogsNativeConfiguration } from './LogsConfigurationNative'; +import type { RumNativeConfiguration } from './RumConfigurationNative'; +import type { TraceNativeConfiguration } from './TraceConfigurationNative'; + +/** + * A configuration object to initialize Datadog's features. + */ +export class DdSdkNativeConfiguration { + constructor( + readonly additionalConfiguration: object, + readonly clientToken: string, + readonly env: string, + readonly site: string, + readonly service: string | undefined, + readonly verbosity: string | undefined, + readonly trackingConsent: string, + readonly uploadFrequency: string, + readonly batchSize: string, + readonly batchProcessingLevel: BatchProcessingLevel, + readonly proxyConfiguration: + | { + type: string; + address: string; + port: number; + username?: string; + password?: string; + } + | undefined, + readonly attributeEncoders: AttributeEncoder[], + readonly rumConfiguration: RumNativeConfiguration | undefined, + readonly logsConfiguration: LogsNativeConfiguration | undefined, + readonly traceConfiguration: TraceNativeConfiguration | undefined, + readonly configurationForTelemetry: { + initializationType: string; + trackErrors: boolean; + trackInteractions: boolean; + trackNetworkRequests: boolean; + reactVersion: string; + reactNativeVersion: string; + } // eslint-disable-next-line no-empty-function + ) {} +} diff --git a/packages/core/src/config/features/LogsConfiguration.ts b/packages/core/src/config/features/LogsConfiguration.ts new file mode 100644 index 000000000..f0af2278a --- /dev/null +++ b/packages/core/src/config/features/LogsConfiguration.ts @@ -0,0 +1,38 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { LogEventMapper } from '../../types'; + +import type { + LogsConfigurationOptions, + LogsConfigurationType +} from './LogsConfiguration.type'; + +const DEFAULTS = { + bundleLogsWithRum: true, + bundleLogsWithTraces: true, + customEndpoint: undefined, + logEventMapper: null +}; + +export class LogsConfiguration implements LogsConfigurationType { + // Bundle Logs with RUM + public bundleLogsWithRum: boolean = DEFAULTS.bundleLogsWithRum; + + // Bundle Logs with Traces + public bundleLogsWithTraces: boolean = DEFAULTS.bundleLogsWithTraces; + + // Custom Endpoint URL + public customEndpoint?: string = DEFAULTS.customEndpoint; + + // Custom Log Event Mapper + public logEventMapper: LogEventMapper | null = DEFAULTS.logEventMapper; + + constructor(options: LogsConfigurationOptions = {}) { + Object.assign(this, options); + } +} + +export { DEFAULTS as LOGS_DEFAULTS }; diff --git a/packages/core/src/config/features/LogsConfiguration.type.ts b/packages/core/src/config/features/LogsConfiguration.type.ts new file mode 100644 index 000000000..1256172a5 --- /dev/null +++ b/packages/core/src/config/features/LogsConfiguration.type.ts @@ -0,0 +1,33 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { LogEventMapper } from '../../types'; + +/** + * Logs configuration options. + */ +export interface LogsConfigurationType { + /** + * Enables correlation between logs and RUM. + */ + bundleLogsWithRum?: boolean; + + /** + * Enables correlation between logs and traces. + */ + bundleLogsWithTraces?: boolean; + + /** + * Sets a target custom server for Logs. + */ + customEndpoint?: string; + + /** + * Custom mapper to transform log events. + */ + logEventMapper?: LogEventMapper | null; +} + +export type LogsConfigurationOptions = LogsConfigurationType; diff --git a/packages/core/src/config/features/LogsConfigurationNative.ts b/packages/core/src/config/features/LogsConfigurationNative.ts new file mode 100644 index 000000000..fc6967d3a --- /dev/null +++ b/packages/core/src/config/features/LogsConfigurationNative.ts @@ -0,0 +1,10 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export type LogsNativeConfiguration = { + readonly bundleLogsWithRum: boolean; + readonly bundleLogsWithTraces: boolean; + readonly customEndpoint: string; +}; diff --git a/packages/core/src/config/features/RumConfiguration.ts b/packages/core/src/config/features/RumConfiguration.ts new file mode 100644 index 000000000..3ab70409c --- /dev/null +++ b/packages/core/src/config/features/RumConfiguration.ts @@ -0,0 +1,137 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { ActionEventMapper } from '../../rum/eventMappers/actionEventMapper'; +import type { ErrorEventMapper } from '../../rum/eventMappers/errorEventMapper'; +import type { ResourceEventMapper } from '../../rum/eventMappers/resourceEventMapper'; +import type { FirstPartyHost } from '../../rum/types'; +import { VitalsUpdateFrequency } from '../types'; + +import type { + RumConfigurationOptions, + RumConfigurationType +} from './RumConfiguration.type'; + +const DEFAULTS = { + actionEventMapper: null, + actionNameAttribute: undefined, + appHangThreshold: undefined, + customEndpoint: undefined, + errorEventMapper: null, + getFirstPartyHosts: () => [], + initialResourceThreshold: undefined, + longTaskThresholdMs: 0, + nativeCrashReportEnabled: false, + nativeInteractionTracking: false, + nativeLongTaskThresholdMs: 200, + nativeViewTracking: false, + resourceEventMapper: null, + resourceTraceSampleRate: 100.0, + sessionSampleRate: 100.0, + telemetrySampleRate: 20.0, + trackBackgroundEvents: false, + trackErrors: false, + trackFrustrations: true, + trackInteractions: false, + trackMemoryWarnings: true, + trackNonFatalAnrs: undefined, + trackResources: false, + trackWatchdogTerminations: false, + useAccessibilityLabel: true, + vitalsUpdateFrequency: VitalsUpdateFrequency.AVERAGE +}; + +export class RumConfiguration implements RumConfigurationType { + // Custom Action Event Mapper + public actionEventMapper: ActionEventMapper | null = + DEFAULTS.actionEventMapper; + + // Custom Action Name Attribute for interaction tracking + public actionNameAttribute?: string = DEFAULTS.actionNameAttribute; + + // Custom App Hang Threshold + public appHangThreshold?: number = DEFAULTS.appHangThreshold; + + // Custom Endpoint URL + public customEndpoint?: string = DEFAULTS.customEndpoint; + + // Custom Error Event Mapper + public errorEventMapper: ErrorEventMapper | null = + DEFAULTS.errorEventMapper; + + // List of First Party Hosts + public firstPartyHosts: FirstPartyHost[] = DEFAULTS.getFirstPartyHosts(); + + // Initial Resource Threshold + public initialResourceThreshold?: number = + DEFAULTS.initialResourceThreshold; + + // Long Task Threshold in milliseconds + public longTaskThresholdMs: number | 0 | false = + DEFAULTS.longTaskThresholdMs; + + // Native Crash Report enabled + public nativeCrashReportEnabled: boolean = + DEFAULTS.nativeCrashReportEnabled; + + // Native Interaction Tracking enabled + public nativeInteractionTracking: boolean = + DEFAULTS.nativeInteractionTracking; + + // Native Long Task Threshold in milliseconds + public nativeLongTaskThresholdMs: number = + DEFAULTS.nativeLongTaskThresholdMs; + + // Native View Tracking enabled + public nativeViewTracking: boolean = DEFAULTS.nativeViewTracking; + + // Custom Resource Event Mapper + public resourceEventMapper: ResourceEventMapper | null = + DEFAULTS.resourceEventMapper; + + // Resource Trace Sample Rate for trackResources + public resourceTraceSampleRate: number = DEFAULTS.resourceTraceSampleRate; + + // Session Sample Rate for RUM + public sessionSampleRate: number = DEFAULTS.sessionSampleRate; + + // Telemetry Sample Rate + public telemetrySampleRate: number = DEFAULTS.telemetrySampleRate; + + // Track Background Events Enabled + public trackBackgroundEvents: boolean = DEFAULTS.trackBackgroundEvents; + + // Track Frustrations Enabled + public trackFrustrations: boolean = DEFAULTS.trackFrustrations; + + // Track Memory Warnings Enabled + public trackMemoryWarnings: boolean = DEFAULTS.trackMemoryWarnings; + + // Track non-fatal ANRs enabled + public trackNonFatalAnrs?: boolean = DEFAULTS.trackNonFatalAnrs; + + // Track Watchdog Terminations enabled + public trackWatchdogTerminations: boolean = + DEFAULTS.trackWatchdogTerminations; + + // Use Accessibility Label enabled for interaction tracking + public useAccessibilityLabel: boolean = DEFAULTS.useAccessibilityLabel; + + // Custom Vitals Update Frequency + public vitalsUpdateFrequency: VitalsUpdateFrequency = + DEFAULTS.vitalsUpdateFrequency; + + constructor( + readonly applicationId: string, + readonly trackInteractions: boolean = DEFAULTS.trackInteractions, + readonly trackResources: boolean = DEFAULTS.trackResources, + readonly trackErrors: boolean = DEFAULTS.trackErrors, + options: RumConfigurationOptions = {} + ) { + Object.assign(this, options); + } +} + +export { DEFAULTS as RUM_DEFAULTS }; diff --git a/packages/core/src/config/features/RumConfiguration.type.ts b/packages/core/src/config/features/RumConfiguration.type.ts new file mode 100644 index 000000000..0a2094ece --- /dev/null +++ b/packages/core/src/config/features/RumConfiguration.type.ts @@ -0,0 +1,164 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { ActionEventMapper } from '../../rum/eventMappers/actionEventMapper'; +import type { ErrorEventMapper } from '../../rum/eventMappers/errorEventMapper'; +import type { ResourceEventMapper } from '../../rum/eventMappers/resourceEventMapper'; +import type { FirstPartyHost } from '../../rum/types'; +import type { VitalsUpdateFrequency } from '../types'; + +/** + * Required RUM configuration values. + */ +export interface RumConfigurationRequired { + /** + * The Datadog RUM application ID. + */ + applicationId: string; + + /** + * Enables tracking of user interactions. + * Defaults to the SDK-defined value. + */ + trackInteractions?: boolean; + + /** + * Enables tracking of network resources. + * Defaults to the SDK-defined value. + */ + trackResources?: boolean; + + /** + * Enables tracking of errors. + * Defaults to the SDK-defined value. + */ + trackErrors?: boolean; +} + +/** + * Optional RUM configuration values. + */ +export interface RumConfigurationOptions { + /** + * Custom mapper to transform RUM action events. + */ + actionEventMapper?: ActionEventMapper | null; + + /** + * Custom attribute used to name RUM actions. + */ + actionNameAttribute?: string; + + /** + * App hang threshold in seconds for non-fatal app hangs on iOS. + */ + appHangThreshold?: number; + + /** + * Sets a target custom server for RUM. + */ + customEndpoint?: string; + + /** + * Custom mapper to transform RUM error events. + */ + errorEventMapper?: ErrorEventMapper | null; + + /** + * List of backend hosts used to enable tracing. + */ + firstPartyHosts?: FirstPartyHost[]; + + /** + * Initial resource collection threshold in seconds. + */ + initialResourceThreshold?: number; + + /** + * Threshold for JavaScript long task reporting in milliseconds. + */ + longTaskThresholdMs?: number | false; + + /** + * Enables native crash reporting. + */ + nativeCrashReportEnabled?: boolean; + + /** + * Threshold for native long task reporting in milliseconds. + */ + nativeLongTaskThresholdMs?: number; + + /** + * Enables native interaction tracking. + */ + nativeInteractionTracking?: boolean; + + /** + * Enables native view tracking. + */ + nativeViewTracking?: boolean; + + /** + * Custom mapper to transform RUM resource events. + */ + resourceEventMapper?: ResourceEventMapper | null; + + /** + * Percentage of traced network requests. + */ + resourceTraceSampleRate?: number; + + /** + * Percentage of sampled RUM sessions. + */ + sessionSampleRate?: number; + + /** + * Enables tracking of background RUM events. + */ + trackBackgroundEvents?: boolean; + + /** + * Enables tracking of frustration signals. + */ + trackFrustrations?: boolean; + + /** + * Enables tracking of non-fatal ANRs on Android. + */ + trackNonFatalAnrs?: boolean; + + /** + * Enables tracking of app termination by the iOS watchdog. + */ + trackWatchdogTerminations?: boolean; + + /** + * Sampling rate for internal SDK telemetry. + */ + telemetrySampleRate?: number; + + /** + * Enables tracking of memory warnings (iOS only). + */ + trackMemoryWarnings?: boolean; + + /** + * Enables accessibility label usage for action names. + */ + useAccessibilityLabel?: boolean; + + /** + * Preferred frequency for collecting mobile vitals. + */ + vitalsUpdateFrequency?: VitalsUpdateFrequency; +} + +/** + * Complete RUM configuration type. + */ +export type RumConfigurationType = RumConfigurationRequired & + RumConfigurationOptions; diff --git a/packages/core/src/config/features/RumConfigurationNative.ts b/packages/core/src/config/features/RumConfigurationNative.ts new file mode 100644 index 000000000..155f086e9 --- /dev/null +++ b/packages/core/src/config/features/RumConfigurationNative.ts @@ -0,0 +1,26 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export type RumNativeConfiguration = { + readonly applicationId: string; + readonly trackFrustrations: boolean; + readonly longTaskThresholdMs: number; + readonly sessionSampleRate: number; + readonly resourceTraceSampleRate: number; + readonly vitalsUpdateFrequency: string; + readonly trackBackgroundEvents: boolean; + readonly nativeCrashReportEnabled: boolean; + readonly nativeLongTaskThresholdMs: number; + readonly nativeViewTracking: boolean; + readonly nativeInteractionTracking: boolean; + readonly trackNonFatalAnrs: boolean | undefined; + readonly appHangThreshold: number | undefined; + readonly trackWatchdogTerminations: boolean | undefined; + readonly initialResourceThreshold: number | undefined; + readonly trackMemoryWarnings: boolean; + readonly telemetrySampleRate: number; + readonly customEndpoint: string; + readonly firstPartyHosts: { match: string; propagatorTypes: string[] }[]; +}; diff --git a/packages/core/src/config/features/TraceConfiguration.ts b/packages/core/src/config/features/TraceConfiguration.ts new file mode 100644 index 000000000..d959b54a1 --- /dev/null +++ b/packages/core/src/config/features/TraceConfiguration.ts @@ -0,0 +1,24 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import type { + TraceConfigurationOptions, + TraceConfigurationType +} from './TraceConfiguration.type'; + +const DEFAULTS = { + customEndpoint: undefined +}; + +export class TraceConfiguration implements TraceConfigurationType { + // Custom Endpoint URL + public customEndpoint?: string = DEFAULTS.customEndpoint; + + constructor(options: TraceConfigurationOptions = {}) { + Object.assign(this, options); + } +} + +export { DEFAULTS as TRACE_DEFAULTS }; diff --git a/packages/core/src/config/features/TraceConfiguration.type.ts b/packages/core/src/config/features/TraceConfiguration.type.ts new file mode 100644 index 000000000..0c21aa92a --- /dev/null +++ b/packages/core/src/config/features/TraceConfiguration.type.ts @@ -0,0 +1,17 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * Trace configuration options. + */ +export interface TraceConfigurationType { + /** + * Sets a target custom server for Traces. + */ + customEndpoint?: string; +} + +export type TraceConfigurationOptions = TraceConfigurationType; diff --git a/packages/core/src/config/features/TraceConfigurationNative.ts b/packages/core/src/config/features/TraceConfigurationNative.ts new file mode 100644 index 000000000..514171c94 --- /dev/null +++ b/packages/core/src/config/features/TraceConfigurationNative.ts @@ -0,0 +1,8 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export type TraceNativeConfiguration = { + readonly customEndpoint: string; +}; diff --git a/packages/core/src/config/types/BatchProcessingLevel.ts b/packages/core/src/config/types/BatchProcessingLevel.ts new file mode 100644 index 000000000..04e25a85a --- /dev/null +++ b/packages/core/src/config/types/BatchProcessingLevel.ts @@ -0,0 +1,23 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * Enum specifying the preferred level for processing batches of data. + */ +export enum BatchProcessingLevel { + /** + * Only 1 batch will be sent in a single upload cycle. + */ + LOW = 'LOW', + /** + * 10 batches will be sent in a single upload cycle + */ + MEDIUM = 'MEDIUM', + /** + * 100 batches will be sent in a single upload cycle. + */ + HIGH = 'HIGH' +} diff --git a/packages/core/src/config/types/BatchSize.ts b/packages/core/src/config/types/BatchSize.ts new file mode 100644 index 000000000..f8c7fa3d4 --- /dev/null +++ b/packages/core/src/config/types/BatchSize.ts @@ -0,0 +1,24 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * Enum specifying the Datadog SDK policy when batching data together before uploading it to Datadog servers. + * Smaller batches mean smaller but more network requests, whereas larger batches mean fewer but larger network requests. + */ +export enum BatchSize { + /** + * Upload less frequent, larger batches of data + */ + LARGE = 'LARGE', + /** + * Use default size for batches of data + */ + MEDIUM = 'MEDIUM', + /** + * Upload more frequent, smaller batches of data + */ + SMALL = 'SMALL' +} diff --git a/packages/core/src/config/types/InitializationMode.ts b/packages/core/src/config/types/InitializationMode.ts new file mode 100644 index 000000000..511191122 --- /dev/null +++ b/packages/core/src/config/types/InitializationMode.ts @@ -0,0 +1,9 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export enum InitializationMode { + SYNC = 'SYNC', + ASYNC = 'ASYNC' +} diff --git a/packages/core/src/config/types/InitializationModeForTelemetry.ts b/packages/core/src/config/types/InitializationModeForTelemetry.ts new file mode 100644 index 000000000..da936236c --- /dev/null +++ b/packages/core/src/config/types/InitializationModeForTelemetry.ts @@ -0,0 +1,11 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export type InitializationModeForTelemetry = + | 'LEGACY' + | 'SYNC' + | 'ASYNC' + | 'PARTIAL' + | 'FILE'; diff --git a/packages/core/src/ProxyConfiguration.tsx b/packages/core/src/config/types/ProxyConfiguration.ts similarity index 83% rename from packages/core/src/ProxyConfiguration.tsx rename to packages/core/src/config/types/ProxyConfiguration.ts index 43c9ffda9..d1d93ce63 100644 --- a/packages/core/src/ProxyConfiguration.tsx +++ b/packages/core/src/config/types/ProxyConfiguration.ts @@ -1,3 +1,9 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + /** * Proxy server configuration. */ diff --git a/packages/core/src/SdkVerbosity.tsx b/packages/core/src/config/types/SdkVerbosity.ts similarity index 100% rename from packages/core/src/SdkVerbosity.tsx rename to packages/core/src/config/types/SdkVerbosity.ts diff --git a/packages/core/src/TrackingConsent.tsx b/packages/core/src/config/types/TrackingConsent.ts similarity index 100% rename from packages/core/src/TrackingConsent.tsx rename to packages/core/src/config/types/TrackingConsent.ts diff --git a/packages/core/src/config/types/UploadFrequency.tsx b/packages/core/src/config/types/UploadFrequency.tsx new file mode 100644 index 000000000..b1d800a07 --- /dev/null +++ b/packages/core/src/config/types/UploadFrequency.tsx @@ -0,0 +1,23 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * Enum specifying preferred frequency for uploading batches of data. + */ +export enum UploadFrequency { + /** + * Upload data every 1000ms. + */ + FREQUENT = 'FREQUENT', + /** + * Upload data every 5000ms. + */ + AVERAGE = 'AVERAGE', + /** + * Upload data every 10000ms. + */ + RARE = 'RARE' +} diff --git a/packages/core/src/config/types/VitalsUpdateFrequency.ts b/packages/core/src/config/types/VitalsUpdateFrequency.ts new file mode 100644 index 000000000..171185bff --- /dev/null +++ b/packages/core/src/config/types/VitalsUpdateFrequency.ts @@ -0,0 +1,11 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +export enum VitalsUpdateFrequency { + FREQUENT = 'FREQUENT', + AVERAGE = 'AVERAGE', + RARE = 'RARE', + NEVER = 'NEVER' +} diff --git a/packages/core/src/config/types/index.ts b/packages/core/src/config/types/index.ts new file mode 100644 index 000000000..6df83e28c --- /dev/null +++ b/packages/core/src/config/types/index.ts @@ -0,0 +1,15 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +export { BatchProcessingLevel } from './BatchProcessingLevel'; +export { BatchSize } from './BatchSize'; +export { ProxyConfiguration, ProxyType } from './ProxyConfiguration'; +export { SdkVerbosity } from './SdkVerbosity'; +export { TrackingConsent } from './TrackingConsent'; +export { UploadFrequency } from './UploadFrequency'; +export { VitalsUpdateFrequency } from './VitalsUpdateFrequency'; +export { InitializationMode } from './InitializationMode'; +export type { InitializationModeForTelemetry } from './InitializationModeForTelemetry'; diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 8dcd5ed63..1959ae9c0 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -3,27 +3,33 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ +import { DdSdkReactNative } from './DdSdkReactNative'; +import { InternalLog } from './InternalLog'; +import { DatadogProviderConfiguration } from './config/DatadogProviderConfiguration'; +import { FileBasedConfiguration } from './config/FileBasedConfiguration'; +import type { AutoInstrumentationConfiguration } from './config/async/AutoInstrumentationConfiguration'; +import type { PartialInitializationConfiguration } from './config/async/PartialInitializationConfiguration'; +import type { CoreConfigurationOptions } from './config/features/CoreConfiguration.type'; +import { CoreConfiguration } from './config/features/CoreConfiguration'; +import type { LogsConfigurationOptions } from './config/features/LogsConfiguration.type'; +import { LogsConfiguration } from './config/features/LogsConfiguration'; +import type { RumConfigurationOptions } from './config/features/RumConfiguration.type'; +import { RumConfiguration } from './config/features/RumConfiguration'; +import type { TraceConfigurationOptions } from './config/features/TraceConfiguration.type'; +import { TraceConfiguration } from './config/features/TraceConfiguration'; +import { + ProxyConfiguration, + ProxyType +} from './config/types/ProxyConfiguration'; +import { SdkVerbosity } from './config/types/SdkVerbosity'; +import { TrackingConsent } from './config/types/TrackingConsent'; import { BatchProcessingLevel, BatchSize, - DatadogProviderConfiguration, - CoreConfiguration, - RumConfiguration, - LogsConfiguration, - TraceConfiguration, InitializationMode, UploadFrequency, VitalsUpdateFrequency -} from './DdSdkReactNativeConfiguration'; -import type { - AutoInstrumentationConfiguration, - PartialInitializationConfiguration -} from './DdSdkReactNativeConfiguration'; -import { DdSdkReactNative } from './DdSdkReactNative'; -import { InternalLog } from './InternalLog'; -import { ProxyConfiguration, ProxyType } from './ProxyConfiguration'; -import { SdkVerbosity } from './SdkVerbosity'; -import { TrackingConsent } from './TrackingConsent'; +} from './config/types'; import { DdLogs } from './logs/DdLogs'; import { DdRum } from './rum/DdRum'; import { DdBabelInteractionTracking } from './rum/instrumentation/interactionTracking/DdBabelInteractionTracking'; @@ -43,7 +49,6 @@ import type { FirstPartyHost } from './rum/types'; import { PropagatorType, RumActionType } from './rum/types'; import { DatadogProvider } from './sdk/DatadogProvider/DatadogProvider'; import { DdSdk } from './sdk/DdSdk'; -import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { DdTrace } from './trace/DdTrace'; import { ErrorSource, FeatureOperationFailure } from './types'; import { DefaultTimeProvider } from './utils/time-provider/DefaultTimeProvider'; @@ -93,5 +98,9 @@ export type { Timestamp, FirstPartyHost, AutoInstrumentationConfiguration, - PartialInitializationConfiguration + PartialInitializationConfiguration, + CoreConfigurationOptions, + RumConfigurationOptions, + LogsConfigurationOptions, + TraceConfigurationOptions }; diff --git a/packages/core/src/logs/DdLogs.ts b/packages/core/src/logs/DdLogs.ts index f00cbfdfa..14c2ca6ee 100644 --- a/packages/core/src/logs/DdLogs.ts +++ b/packages/core/src/logs/DdLogs.ts @@ -6,7 +6,7 @@ import { DdAttributes } from '../DdAttributes'; import { DATADOG_MESSAGE_PREFIX, InternalLog } from '../InternalLog'; -import { SdkVerbosity } from '../SdkVerbosity'; +import { SdkVerbosity } from '../config/types/SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeLogsType } from '../nativeModulesTypes'; import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; diff --git a/packages/core/src/logs/__tests__/DdLogs.test.ts b/packages/core/src/logs/__tests__/DdLogs.test.ts index 4c40a3d41..c241fa3bc 100644 --- a/packages/core/src/logs/__tests__/DdLogs.test.ts +++ b/packages/core/src/logs/__tests__/DdLogs.test.ts @@ -6,13 +6,12 @@ import { NativeModules } from 'react-native'; -import { - CoreConfiguration, - RumConfiguration -} from '../../DdSdkReactNativeConfiguration'; import { DdSdkReactNative } from '../../DdSdkReactNative'; import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { CoreConfiguration } from '../../config/features/CoreConfiguration'; +import { LogsConfiguration } from '../../config/features/LogsConfiguration'; +import { RumConfiguration } from '../../config/features/RumConfiguration'; +import { SdkVerbosity } from '../../config/types'; import type { DdNativeLogsType } from '../../nativeModulesTypes'; import { ErrorSource } from '../../types'; import type { LogEventMapper, LogEvent } from '../../types'; @@ -214,13 +213,15 @@ describe('DdLogs', () => { ); // Register log event mapper to filter console log events - configuration.logEventMapper = logEvent => { - if (logEvent.source === ErrorSource.CONSOLE) { - return null; - } + configuration.logsConfiguration = new LogsConfiguration({ + logEventMapper: logEvent => { + if (logEvent.source === ErrorSource.CONSOLE) { + return null; + } - return logEvent; - }; + return logEvent; + } + }); NativeModules.DdSdk.initialize.mockResolvedValue(null); diff --git a/packages/core/src/nativeModulesTypes.ts b/packages/core/src/nativeModulesTypes.ts index f7ac0f1f4..1201ab779 100644 --- a/packages/core/src/nativeModulesTypes.ts +++ b/packages/core/src/nativeModulesTypes.ts @@ -4,11 +4,11 @@ * Copyright 2016-Present Datadog, Inc. */ +import type { DdSdkNativeConfiguration } from './config/features/CoreConfigurationNative'; import type { Spec as NativeDdLogs } from './specs/NativeDdLogs'; import type { Spec as NativeDdRum } from './specs/NativeDdRum'; import type { Spec as NativeDdSdk } from './specs/NativeDdSdk'; import type { Spec as NativeDdTrace } from './specs/NativeDdTrace'; -import type { DdSdkNativeConfiguration } from './types'; /** * In this file, native modules types extend the specs for TurboModules. diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 36f116bbf..4241f9282 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -7,7 +7,7 @@ import type { GestureResponderEvent } from 'react-native'; import { DdAttributes } from '../DdAttributes'; import { InternalLog } from '../InternalLog'; -import { SdkVerbosity } from '../SdkVerbosity'; +import { SdkVerbosity } from '../config/types/SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeRumType } from '../nativeModulesTypes'; import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index 9296ad5a2..67e7d8683 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -8,7 +8,7 @@ import { NativeModules } from 'react-native'; import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types'; import { BufferSingleton } from '../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { NativeDdSdk } from '../../sdk/DdSdkInternal'; import { GlobalState } from '../../sdk/GlobalState/GlobalState'; diff --git a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx index b80309858..9cf1f540c 100644 --- a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx +++ b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx @@ -7,7 +7,7 @@ import type { ErrorHandlerCallback } from 'react-native'; import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; import { errorEncoder } from '../../sdk/AttributesEncoding/defaultEncoders'; import { ERROR_DEFAULT_NAME, diff --git a/packages/core/src/rum/instrumentation/interactionTracking/DdEventsInterceptor.tsx b/packages/core/src/rum/instrumentation/interactionTracking/DdEventsInterceptor.tsx index 9447ef7db..d4185347f 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/DdEventsInterceptor.tsx +++ b/packages/core/src/rum/instrumentation/interactionTracking/DdEventsInterceptor.tsx @@ -7,7 +7,7 @@ import type { GestureResponderEvent } from 'react-native'; import { InternalLog } from '../../../InternalLog'; -import { SdkVerbosity } from '../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../config/types/SdkVerbosity'; import { DdRum } from '../../DdRum'; import { RumActionType } from '../../types'; diff --git a/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx b/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx index ed09883e7..f1ad64ef6 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx +++ b/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { InternalLog } from '../../../InternalLog'; -import { SdkVerbosity } from '../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../config/types/SdkVerbosity'; import { getErrorMessage } from '../../../sdk/AttributesEncoding/errorUtils'; import { NativeDdSdk } from '../../../sdk/DdSdkInternal'; import { BABEL_PLUGIN_TELEMETRY } from '../../constants'; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/DdRumResourceTracking.tsx b/packages/core/src/rum/instrumentation/resourceTracking/DdRumResourceTracking.tsx index d515dc98e..2a1c53e28 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/DdRumResourceTracking.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/DdRumResourceTracking.tsx @@ -7,17 +7,15 @@ import BigInt from 'big-integer'; import { InternalLog } from '../../../InternalLog'; -import { SdkVerbosity } from '../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../config/types/SdkVerbosity'; import { getGlobalInstance } from '../../../utils/singletonUtils'; import type { FirstPartyHost } from '../../types'; +import { DistributedTracingSampling } from './distributedTracing/distributedTracingSampling'; import { firstPartyHostsRegexMapBuilder } from './distributedTracing/firstPartyHosts'; -import { ResourceReporter } from './requestProxy/XHRProxy/DatadogRumResource/ResourceReporter'; -import { filterDevResource } from './requestProxy/XHRProxy/DatadogRumResource/internalDevResourceBlocklist'; import { XHRProxy } from './requestProxy/XHRProxy/XHRProxy'; import type { RequestProxy } from './requestProxy/interfaces/RequestProxy'; -export const MAX_TRACE_ID = BigInt.one.shiftLeft(64).minus(BigInt.one); const RUM_RESOURCE_TRACKING_MODULE = 'com.datadog.reactnative.rum.resource_tracking'; @@ -41,10 +39,10 @@ class RumResourceTracking { * Starts tracking resources and sends a RUM Resource event every time a network request is detected. */ startTracking({ - tracingSamplingRate, + resourceTraceSampleRate, firstPartyHosts }: { - tracingSamplingRate: number; + resourceTraceSampleRate: number; firstPartyHosts: FirstPartyHost[]; }): void { // extra safety to avoid proxying the XHR class twice @@ -56,12 +54,9 @@ class RumResourceTracking { return; } - this._requestProxy = new XHRProxy({ - xhrType: XMLHttpRequest, - resourceReporter: new ResourceReporter([filterDevResource]) - }); + this._requestProxy = XHRProxy.createWithResourceReporter(); this._requestProxy.onTrackingStart({ - tracingSamplingRate, + tracingSamplingRate: resourceTraceSampleRate, firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder( firstPartyHosts ) @@ -73,8 +68,8 @@ class RumResourceTracking { ); this._isTracking = true; - this._maxSampledTraceId = RumResourceTracking.getMaxTraceId( - tracingSamplingRate + DistributedTracingSampling.setResourceTraceSampleRate( + resourceTraceSampleRate ); } @@ -88,10 +83,6 @@ class RumResourceTracking { this._maxSampledTraceId = null; } } - - private static getMaxTraceId(sampleRate: number): BigInt.BigInteger { - return BigInt(MAX_TRACE_ID.toJSNumber() * (sampleRate / 100.0)); - } } export const DdRumResourceTracking = getGlobalInstance( diff --git a/packages/core/src/rum/instrumentation/resourceTracking/__tests__/DdRumResourceTracking.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/__tests__/DdRumResourceTracking.test.ts index 1d75f0500..c2ab68d17 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/__tests__/DdRumResourceTracking.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/__tests__/DdRumResourceTracking.test.ts @@ -41,7 +41,7 @@ describe('DdRumResourceTracking', () => { // GIVEN global.XMLHttpRequest = XMLHttpRequestMock; DdRumResourceTracking.startTracking({ - tracingSamplingRate: 100, + resourceTraceSampleRate: 100, firstPartyHosts: [ { match: 'example.com', @@ -73,7 +73,7 @@ describe('DdRumResourceTracking', () => { // GIVEN global.XMLHttpRequest = XMLHttpRequestMock; DdRumResourceTracking.startTracking({ - tracingSamplingRate: 100, + resourceTraceSampleRate: 100, firstPartyHosts: [ { match: 'example.com', diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingContext.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingContext.ts index 0f0617408..8cfd27e23 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingContext.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingContext.ts @@ -8,7 +8,7 @@ import { formatBaggageHeader } from '../requestProxy/XHRProxy/baggageHeaderUtils import { DatadogTracingIdentifier } from './DatadogTracingIdentifier'; import type { SpanId, TraceId } from './TracingIdentifier'; -import { BAGGAGE_HEADER_KEY } from './distributedTracingHeaders'; +import { BAGGAGE_HEADER_KEY } from './headers'; /** * An object that contains the tracing attributes as headers for network requests and attributes diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/TracingIdentifier.tsx b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/TracingIdentifier.tsx index 3cd7e2de3..a3b590371 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/TracingIdentifier.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/TracingIdentifier.tsx @@ -7,6 +7,8 @@ import type { BigInteger } from 'big-integer'; import BigInt from 'big-integer'; +export const MAX_TRACE_ID = BigInt.one.shiftLeft(64).minus(BigInt.one); + /** * Available formats for representing the {@link TracingIdentifier} as a string. */ diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts index 28198d751..dda5ad88a 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/__tests__/distributedTracing.test.ts @@ -14,7 +14,7 @@ import { TracingIdentifier, TracingIdFormat } from '../TracingIdentifier'; -import { shouldSampleTrace } from '../distributedTracing'; +import { DistributedTracingSampling } from '../distributedTracingSampling'; import { TracingIdentifierUtils } from './__utils__/TracingIdentifierUtils'; @@ -399,7 +399,7 @@ describe('Sampling behavior', () => { inputs.forEach(([identifier, sampleRate, expected]) => { it(`sampling decision is deterministic for traceId=${identifier.toString()} and sampleRate=${sampleRate}`, () => { DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -408,7 +408,7 @@ describe('Sampling behavior', () => { TracingIdType.trace ) as TraceId; - const shouldSample = shouldSampleTrace( + const shouldSample = DistributedTracingSampling.shouldSampleTrace( sampleRate, null, // no sessionId -> fallback to traceId-based sampling tracingId @@ -504,7 +504,7 @@ describe('Sampling behavior', () => { it(`sampling decision is deterministic for ${sessionId} and sampleRate=${sampleRate}`, () => { // see note below DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -513,7 +513,7 @@ describe('Sampling behavior', () => { TracingIdType.trace ) as TraceId; - const shouldSample = shouldSampleTrace( + const shouldSample = DistributedTracingSampling.shouldSampleTrace( sampleRate, sessionId, tracingId @@ -548,7 +548,7 @@ describe('Sampling behavior', () => { const sampleRate = 100; DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -560,7 +560,7 @@ describe('Sampling behavior', () => { ) as TraceId; const sessionId = uuidv4(); - const shouldSample = shouldSampleTrace( + const shouldSample = DistributedTracingSampling.shouldSampleTrace( sampleRate, sessionId, trace @@ -574,7 +574,7 @@ describe('Sampling behavior', () => { const sampleRate = 0; DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -586,7 +586,7 @@ describe('Sampling behavior', () => { ) as TraceId; const sessionId = uuidv4(); - const shouldSample = shouldSampleTrace( + const shouldSample = DistributedTracingSampling.shouldSampleTrace( sampleRate, sessionId, trace @@ -600,7 +600,7 @@ describe('Sampling behavior', () => { const sampleRate = 23; DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -615,7 +615,13 @@ describe('Sampling behavior', () => { ) as TraceId; const sessionId = uuidv4(); - if (shouldSampleTrace(sampleRate, sessionId, trace)) { + if ( + DistributedTracingSampling.shouldSampleTrace( + sampleRate, + sessionId, + trace + ) + ) { sampleCount++; } else { noSampleCount++; @@ -630,7 +636,7 @@ describe('Sampling behavior', () => { const sampleRate = 85; DdRumResourceTracking.startTracking({ - tracingSamplingRate: sampleRate, + resourceTraceSampleRate: sampleRate, firstPartyHosts: [] }); @@ -645,7 +651,13 @@ describe('Sampling behavior', () => { ) as TraceId; const sessionId = uuidv4(); - if (shouldSampleTrace(sampleRate, sessionId, trace)) { + if ( + DistributedTracingSampling.shouldSampleTrace( + sampleRate, + sessionId, + trace + ) + ) { sampleCount++; } else { noSampleCount++; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx index 7ad492362..8c1afdfd5 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx @@ -3,40 +3,15 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - -import BigInt from 'big-integer'; - import type { PropagatorType } from '../../../types'; -import { DdRumResourceTracking, MAX_TRACE_ID } from '../DdRumResourceTracking'; import type { RegexMap } from '../requestProxy/interfaces/RequestProxy'; import { TracingIdentifier } from './TracingIdentifier'; -import type { SpanId, TraceId } from './TracingIdentifier'; +import type { DdRumResourceTracingAttributes } from './distributedTracingAttributes'; +import { DistributedTracingSampling } from './distributedTracingSampling'; import type { Hostname } from './firstPartyHosts'; import { getPropagatorsForHost } from './firstPartyHosts'; -const KNUTH_FACTOR = BigInt('1111111111111111111'); - -export type DdRumResourceTracingAttributes = - | { - tracingStrategy: 'KEEP'; - traceId: TraceId; - spanId: SpanId; - samplingPriorityHeader: '1' | '0'; - rulePsr: number; - propagatorTypes: PropagatorType[]; - rumSessionId?: string; - userId?: string; - accountId?: string; - baggageHeaders?: Set; - } - | { - tracingStrategy: 'DISCARD'; - traceId?: void; - spanId?: void; - samplingPriorityHeader: '0'; - }; - const DISCARDED_TRACE_ATTRIBUTES: DdRumResourceTracingAttributes = { samplingPriorityHeader: '0', tracingStrategy: 'DISCARD' @@ -76,48 +51,6 @@ export const getTracingAttributes = ({ return DISCARDED_TRACE_ATTRIBUTES; }; -export const shouldSampleTrace = ( - tracingSamplingRate: number, - sessionId: string | null | undefined, - traceId: TraceId -): boolean => { - if (tracingSamplingRate >= 100) { - return true; - } - if (tracingSamplingRate <= 0) { - return false; - } - - // Offer consistent sampling for the same trace id across different environments. The rule is: - // - // (identifier * knuthFactor) < max_trace_id - // - // We use the low 48 bits from the session id if it exists, or the low bits of the trace id if it doesn't - let lowBits: BigInt.BigInteger | null = null; - - if (sessionId != null) { - const uuidParts = sessionId.split('-'); - if (uuidParts.length === 5) { - const lastPart = uuidParts[4]; - try { - // Parse last UUID part as hex into bigint - lowBits = BigInt(`${lastPart}`, 16); - } catch { - // ignore parse errors, lowBits stays null - } - } - } - - if (lowBits === null) { - lowBits = traceId.id.and(MAX_TRACE_ID); - } - - return lowBits - .multiply(KNUTH_FACTOR) - .and(MAX_TRACE_ID) - .lesser(DdRumResourceTracking.maxSampledTraceId); -}; - export const generateTracingAttributesWithSampling = ( tracingSamplingRate: number, propagatorTypes: PropagatorType[], @@ -131,7 +64,7 @@ export const generateTracingAttributesWithSampling = ( const traceId = TracingIdentifier.createTraceId(); - const isSampled = shouldSampleTrace( + const isSampled = DistributedTracingSampling.shouldSampleTrace( tracingSamplingRate, rumSessionId, traceId diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingAttributes.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingAttributes.ts new file mode 100644 index 000000000..b67bc4cc9 --- /dev/null +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingAttributes.ts @@ -0,0 +1,29 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import type { PropagatorType } from '../../../types'; + +import type { SpanId, TraceId } from './TracingIdentifier'; + +export type DdRumResourceTracingAttributes = + | { + tracingStrategy: 'KEEP'; + traceId: TraceId; + spanId: SpanId; + samplingPriorityHeader: '1' | '0'; + rulePsr: number; + propagatorTypes: PropagatorType[]; + rumSessionId?: string; + userId?: string; + accountId?: string; + baggageHeaders?: Set; + } + | { + tracingStrategy: 'DISCARD'; + traceId?: void; + spanId?: void; + samplingPriorityHeader: '0'; + }; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts index 033775a17..8c3ce0ed8 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts @@ -10,37 +10,31 @@ import { URLHostParser } from '../requestProxy/XHRProxy/URLHostParser'; import { DatadogTracingContext } from './DatadogTracingContext'; import { TracingIdFormat } from './TracingIdentifier'; import type { TraceId, SpanId } from './TracingIdentifier'; +import type { DdRumResourceTracingAttributes } from './distributedTracingAttributes'; import { generateTracingAttributesWithSampling, getTracingAttributes } from './distributedTracing'; -import type { DdRumResourceTracingAttributes } from './distributedTracing'; import { firstPartyHostsRegexMapBuilder } from './firstPartyHosts'; - -export const SAMPLING_PRIORITY_HEADER_KEY = 'x-datadog-sampling-priority'; -/** - * Datadog headers - */ -export const ORIGIN_HEADER_KEY = 'x-datadog-origin'; -export const ORIGIN_RUM = 'rum'; -export const TRACE_ID_HEADER_KEY = 'x-datadog-trace-id'; -export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id'; -export const TAGS_HEADER_KEY = 'x-datadog-tags'; -export const DD_TRACE_ID_TAG = '_dd.p.tid'; -export const DD_RUM_SESSION_ID_TAG = 'session.id'; -export const DD_RUM_USER_ID_TAG = 'user.id'; -export const DD_RUM_ACCOUNT_ID_TAG = 'account.id'; - -/** - * OTel headers - */ -export const TRACECONTEXT_HEADER_KEY = 'traceparent'; -export const TRACESTATE_HEADER_KEY = 'tracestate'; -export const BAGGAGE_HEADER_KEY = 'baggage'; -export const B3_HEADER_KEY = 'b3'; -export const B3_MULTI_TRACE_ID_HEADER_KEY = 'X-B3-TraceId'; -export const B3_MULTI_SPAN_ID_HEADER_KEY = 'X-B3-SpanId'; -export const B3_MULTI_SAMPLED_HEADER_KEY = 'X-B3-Sampled'; +import { + B3_HEADER_KEY, + B3_MULTI_SAMPLED_HEADER_KEY, + B3_MULTI_SPAN_ID_HEADER_KEY, + B3_MULTI_TRACE_ID_HEADER_KEY, + BAGGAGE_HEADER_KEY, + DD_RUM_ACCOUNT_ID_TAG, + DD_RUM_SESSION_ID_TAG, + DD_RUM_USER_ID_TAG, + DD_TRACE_ID_TAG, + ORIGIN_HEADER_KEY, + ORIGIN_RUM, + PARENT_ID_HEADER_KEY, + SAMPLING_PRIORITY_HEADER_KEY, + TAGS_HEADER_KEY, + TRACECONTEXT_HEADER_KEY, + TRACESTATE_HEADER_KEY, + TRACE_ID_HEADER_KEY +} from './headers'; export const getTracingHeadersFromAttributes = ( tracingAttributes: DdRumResourceTracingAttributes diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingSampling.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingSampling.ts new file mode 100644 index 000000000..6791d5d12 --- /dev/null +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingSampling.ts @@ -0,0 +1,80 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import BigInt from 'big-integer'; + +import { getGlobalInstance } from '../../../../utils/singletonUtils'; + +import { MAX_TRACE_ID } from './TracingIdentifier'; +import type { TraceId } from './TracingIdentifier'; + +const KNUTH_FACTOR = BigInt('1111111111111111111'); + +const DISTRIBUTED_TRACING_SAMPLING_MODULE = + 'com.datadog.reactnative.rum.distributed_tracing_sampling'; + +class _DistributedTracingSampling { + private _maxSampledTraceId: BigInt.BigInteger | null = null; + + get maxSampledTraceId(): BigInt.BigInteger { + return this._maxSampledTraceId ?? BigInt(0); + } + + setResourceTraceSampleRate(resourceTraceSampleRate: number) { + this._maxSampledTraceId = _DistributedTracingSampling.getMaxTraceId( + resourceTraceSampleRate + ); + } + + shouldSampleTrace( + tracingSamplingRate: number, + sessionId: string | null | undefined, + traceId: TraceId + ): boolean { + if (tracingSamplingRate >= 100) { + return true; + } + if (tracingSamplingRate <= 0) { + return false; + } + + // Offer consistent sampling for the same trace id across different environments. The rule is: + // + // (identifier * knuthFactor) < max_trace_id + // + // We use the low 48 bits from the session id if it exists, or the low bits of the trace id if it doesn't + let lowBits: BigInt.BigInteger | null = null; + + if (sessionId != null) { + const uuidParts = sessionId.split('-'); + if (uuidParts.length === 5) { + const lastPart = uuidParts[4]; + try { + // Parse last UUID part as hex into bigint + lowBits = BigInt(`${lastPart}`, 16); + } catch { + // ignore parse errors, lowBits stays null + } + } + } + + if (lowBits === null) { + lowBits = traceId.id.and(MAX_TRACE_ID); + } + + return lowBits + .multiply(KNUTH_FACTOR) + .and(MAX_TRACE_ID) + .lesser(this.maxSampledTraceId); + } + + private static getMaxTraceId(sampleRate: number): BigInt.BigInteger { + return BigInt(MAX_TRACE_ID.toJSNumber() * (sampleRate / 100.0)); + } +} + +export const DistributedTracingSampling = getGlobalInstance( + DISTRIBUTED_TRACING_SAMPLING_MODULE, + () => new _DistributedTracingSampling() +); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/firstPartyHosts.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/firstPartyHosts.ts index cc18ee9c7..015adfc06 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/firstPartyHosts.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/firstPartyHosts.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../../../InternalLog'; -import { SdkVerbosity } from '../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../config/types/SdkVerbosity'; import type { FirstPartyHost, PropagatorType } from '../../../types'; import type { RegexMap } from '../requestProxy/interfaces/RequestProxy'; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts new file mode 100644 index 000000000..4301fc027 --- /dev/null +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/headers.ts @@ -0,0 +1,29 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * Datadog headers + */ +export const SAMPLING_PRIORITY_HEADER_KEY = 'x-datadog-sampling-priority'; +export const ORIGIN_HEADER_KEY = 'x-datadog-origin'; +export const ORIGIN_RUM = 'rum'; +export const TRACE_ID_HEADER_KEY = 'x-datadog-trace-id'; +export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id'; +export const TAGS_HEADER_KEY = 'x-datadog-tags'; +export const DD_TRACE_ID_TAG = '_dd.p.tid'; +export const DD_RUM_SESSION_ID_TAG = 'session.id'; +export const DD_RUM_USER_ID_TAG = 'user.id'; +export const DD_RUM_ACCOUNT_ID_TAG = 'account.id'; + +/** + * OTel headers + */ +export const TRACECONTEXT_HEADER_KEY = 'traceparent'; +export const TRACESTATE_HEADER_KEY = 'tracestate'; +export const BAGGAGE_HEADER_KEY = 'baggage'; +export const B3_HEADER_KEY = 'b3'; +export const B3_MULTI_TRACE_ID_HEADER_KEY = 'X-B3-TraceId'; +export const B3_MULTI_SPAN_ID_HEADER_KEY = 'X-B3-SpanId'; +export const B3_MULTI_SAMPLED_HEADER_KEY = 'X-B3-Sampled'; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/URLHostParser.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/URLHostParser.ts index 720cacaf6..cb45f8a42 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/URLHostParser.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/URLHostParser.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../../../../InternalLog'; -import { SdkVerbosity } from '../../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../../config/types/SdkVerbosity'; import type { Hostname } from '../../distributedTracing/firstPartyHosts'; // matches what is between the first "://" and the next "/", ":" or whitespace diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts index 723ace5ed..6636944f8 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts @@ -10,12 +10,10 @@ import { getCachedSessionId, getCachedUserId } from '../../../../helper'; -import { - BAGGAGE_HEADER_KEY, - getTracingHeadersFromAttributes -} from '../../distributedTracing/distributedTracingHeaders'; -import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracing'; +import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracingAttributes'; +import { getTracingHeadersFromAttributes } from '../../distributedTracing/distributedTracingHeaders'; import { getTracingAttributes } from '../../distributedTracing/distributedTracing'; +import { BAGGAGE_HEADER_KEY } from '../../distributedTracing/headers'; import { DATADOG_GRAPH_QL_OPERATION_NAME_HEADER, DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER, @@ -25,7 +23,8 @@ import { DATADOG_BAGGAGE_HEADER, isDatadogCustomHeader } from '../../headers'; import type { RequestProxyOptions } from '../interfaces/RequestProxy'; import { RequestProxy } from '../interfaces/RequestProxy'; -import type { ResourceReporter } from './DatadogRumResource/ResourceReporter'; +import { ResourceReporter } from './DatadogRumResource/ResourceReporter'; +import { filterDevResource } from './DatadogRumResource/internalDevResourceBlocklist'; import { URLHostParser } from './URLHostParser'; import { formatBaggageHeader } from './baggageHeaderUtils'; import { calculateResponseSize } from './responseSize'; @@ -69,6 +68,13 @@ export class XHRProxy extends RequestProxy { this.providers = providers; } + static createWithResourceReporter() { + return new XHRProxy({ + xhrType: XMLHttpRequest, + resourceReporter: new ResourceReporter([filterDevResource]) + }); + } + onTrackingStart = (context: RequestProxyOptions) => { XHRProxy.originalXhrOpen = this.providers.xhrType.prototype.open; XHRProxy.originalXhrSend = this.providers.xhrType.prototype.send; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index 9103140a2..b82d73127 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -8,7 +8,7 @@ import BigInt from 'big-integer'; import { Platform, NativeModules } from 'react-native'; import { InternalLog } from '../../../../../../InternalLog'; -import { SdkVerbosity } from '../../../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../../../config/types'; import { BufferSingleton } from '../../../../../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { DdRum } from '../../../../../DdRum'; import { @@ -19,6 +19,7 @@ import { import { PropagatorType } from '../../../../../types'; import { XMLHttpRequestMock } from '../../../__tests__/__utils__/XMLHttpRequestMock'; import { TracingIdentifierUtils } from '../../../distributedTracing/__tests__/__utils__/TracingIdentifierUtils'; +import { firstPartyHostsRegexMapBuilder } from '../../../distributedTracing/firstPartyHosts'; import { PARENT_ID_HEADER_KEY, TRACE_ID_HEADER_KEY, @@ -33,8 +34,7 @@ import { TRACESTATE_HEADER_KEY, TAGS_HEADER_KEY, BAGGAGE_HEADER_KEY -} from '../../../distributedTracing/distributedTracingHeaders'; -import { firstPartyHostsRegexMapBuilder } from '../../../distributedTracing/firstPartyHosts'; +} from '../../../distributedTracing/headers'; import { DATADOG_GRAPH_QL_OPERATION_NAME_HEADER, DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER, diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/baggageHeaderUtils.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/baggageHeaderUtils.test.ts index bf2fa5f2b..4489c90c4 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/baggageHeaderUtils.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/baggageHeaderUtils.test.ts @@ -1,5 +1,5 @@ import { InternalLog } from '../../../../../../InternalLog'; -import { SdkVerbosity } from '../../../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../../../config/types'; import { formatBaggageHeader } from '../baggageHeaderUtils'; describe('formatBaggageHeader', () => { diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts index 305bfa896..b9f23e7af 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts @@ -5,12 +5,12 @@ */ import { InternalLog } from '../../../../../InternalLog'; -import { SdkVerbosity } from '../../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../../config/types/SdkVerbosity'; import { DD_RUM_ACCOUNT_ID_TAG, DD_RUM_SESSION_ID_TAG, DD_RUM_USER_ID_TAG -} from '../../distributedTracing/distributedTracingHeaders'; +} from '../../distributedTracing/headers'; // The resulting baggage-string should contain 64 list-members or less (https://www.w3.org/TR/baggage/#limits) const MAX_MEMBERS = 64; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/responseSize.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/responseSize.ts index 495d71ffa..731be8a20 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/responseSize.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/responseSize.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../../../../InternalLog'; -import { SdkVerbosity } from '../../../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../../../config/types/SdkVerbosity'; const MISSING_RESOURCE_SIZE = -1; export const RESOURCE_SIZE_ERROR_MESSAGE = diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RumResource.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RumResource.ts index c3a9939c3..15aab8201 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RumResource.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RumResource.ts @@ -5,7 +5,7 @@ */ import type { ResourceKind } from '../../../../types'; -import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracing'; +import type { DdRumResourceTracingAttributes } from '../../distributedTracing/distributedTracingAttributes'; export interface RUMResource { key: string; diff --git a/packages/core/src/sdk/AttributesEncoding/utils.ts b/packages/core/src/sdk/AttributesEncoding/utils.ts index 11e3adfdf..1187828d6 100644 --- a/packages/core/src/sdk/AttributesEncoding/utils.ts +++ b/packages/core/src/sdk/AttributesEncoding/utils.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; export function warn(text: string) { InternalLog.log(`[ATTRIBUTES] ${text}`, SdkVerbosity.WARN); diff --git a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx index 34a1e623a..186138a00 100644 --- a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx +++ b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; import { setCachedSessionId } from '../../rum/helper'; import { DatadogDefaultEventEmitter } from '../DatadogEventEmitter/DatadogDefaultEventEmitter'; import type { DatadogEventEmitter } from '../DatadogEventEmitter/DatadogEventEmitter'; diff --git a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridgeEvent.tsx b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridgeEvent.tsx index 528e02b83..a41c948a2 100644 --- a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridgeEvent.tsx +++ b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridgeEvent.tsx @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; export class DdSdkInternalNativeBridgeEvent { public readonly eventName: string; diff --git a/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts b/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts index 93f9389b0..071129044 100644 --- a/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts +++ b/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../../InternalLog'; -import { SdkVerbosity } from '../../../SdkVerbosity'; +import { SdkVerbosity } from '../../../config/types/SdkVerbosity'; import { getErrorStackTrace } from '../../AttributesEncoding/errorUtils'; import { NativeDdSdk } from '../../DdSdkInternal'; diff --git a/packages/core/src/sdk/DatadogProvider/DatadogProvider.tsx b/packages/core/src/sdk/DatadogProvider/DatadogProvider.tsx index c0b452f86..a8a5ce880 100644 --- a/packages/core/src/sdk/DatadogProvider/DatadogProvider.tsx +++ b/packages/core/src/sdk/DatadogProvider/DatadogProvider.tsx @@ -6,18 +6,14 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; -import { - DatadogProviderConfiguration, - CoreConfiguration -} from '../../DdSdkReactNativeConfiguration'; -import type { - PartialInitializationConfiguration, - AutoInstrumentationConfiguration -} from '../../DdSdkReactNativeConfiguration'; import { DdSdkReactNative } from '../../DdSdkReactNative'; import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; -import type { FileBasedConfiguration } from '../FileBasedConfiguration/FileBasedConfiguration'; +import { DatadogProviderConfiguration } from '../../config/DatadogProviderConfiguration'; +import type { FileBasedConfiguration } from '../../config/FileBasedConfiguration'; +import type { AutoInstrumentationConfiguration } from '../../config/async/AutoInstrumentationConfiguration'; +import type { PartialInitializationConfiguration } from '../../config/async/PartialInitializationConfiguration'; +import { CoreConfiguration } from '../../config/features/CoreConfiguration'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; import { DatadogProviderState } from './DatadogProviderState'; diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/__utils__/renderWithProvider.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/__utils__/renderWithProvider.tsx index 75e25fef2..80069c5b1 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/__utils__/renderWithProvider.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/__utils__/renderWithProvider.tsx @@ -8,11 +8,9 @@ import { render } from '@testing-library/react-native'; import { Animated, Button, InteractionManager, Text, View } from 'react-native'; import React, { useState } from 'react'; -import type { AutoInstrumentationConfiguration } from '../../../../DdSdkReactNativeConfiguration'; -import { - RumConfiguration, - DatadogProviderConfiguration -} from '../../../../DdSdkReactNativeConfiguration'; +import { DatadogProviderConfiguration } from '../../../../config/DatadogProviderConfiguration'; +import type { AutoInstrumentationConfiguration } from '../../../../config/async/AutoInstrumentationConfiguration'; +import { RumConfiguration } from '../../../../config/features/RumConfiguration'; import { DatadogProvider } from '../../DatadogProvider'; const DefaultTestApp = () => { diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx index 9f43410ef..596ab9532 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx @@ -7,9 +7,9 @@ import { version as reactNativeVersion } from 'react-native/package.json'; import { NativeModules } from 'react-native'; -import { InitializationMode } from '../../../DdSdkReactNativeConfiguration'; +import { InitializationMode } from '../../../config/types'; import { DdRum } from '../../../rum/DdRum'; -import { RumActionType } from '../../../rum/types'; +import { PropagatorType, RumActionType } from '../../../rum/types'; import { DdTrace } from '../../../trace/DdTrace'; import { DefaultTimeProvider } from '../../../utils/time-provider/DefaultTimeProvider'; import { GlobalState } from '../../GlobalState/GlobalState'; @@ -81,19 +81,24 @@ describe('DatadogProvider', () => { "trackNetworkRequests": false, }, "env": "fakeEnv", - "firstPartyHosts": [], "logsConfiguration": undefined, - "nativeCrashReportEnabled": false, - "nativeLongTaskThresholdMs": 200, "proxyConfiguration": undefined, "rumConfiguration": RumConfiguration { "actionEventMapper": null, + "actionNameAttribute": undefined, + "appHangThreshold": undefined, "applicationId": "fakeApplicationId", + "customEndpoint": undefined, "errorEventMapper": null, - "longTaskThresholdMs": 200, + "firstPartyHosts": [], + "initialResourceThreshold": undefined, + "longTaskThresholdMs": 0, + "nativeCrashReportEnabled": false, "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 200, "nativeViewTracking": false, "resourceEventMapper": null, + "resourceTraceSampleRate": 100, "sessionSampleRate": 100, "telemetrySampleRate": 20, "trackBackgroundEvents": false, @@ -101,8 +106,10 @@ describe('DatadogProvider', () => { "trackFrustrations": true, "trackInteractions": true, "trackMemoryWarnings": true, + "trackNonFatalAnrs": undefined, "trackResources": false, "trackWatchdogTerminations": false, + "useAccessibilityLabel": true, "vitalsUpdateFrequency": "AVERAGE", }, "service": undefined, @@ -193,12 +200,19 @@ describe('DatadogProvider', () => { rumConfiguration: { trackErrors: true, trackResources: true, - trackInteractions: true - }, - firstPartyHosts: ['api.com'], - traceConfiguration: { - resourceTraceSampleRate: 100 + trackInteractions: true, + resourceTraceSampleRate: 100, + firstPartyHosts: [ + { + match: 'api.com', + propagatorTypes: [ + PropagatorType.DATADOG, + PropagatorType.TRACECONTEXT + ] + } + ] }, + traceConfiguration: {}, logsConfiguration: {} } }); diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/initializationModes.test.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/initializationModes.test.tsx index e64779f91..75619b6d7 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/initializationModes.test.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/initializationModes.test.tsx @@ -7,10 +7,11 @@ import { fireEvent } from '@testing-library/react-native'; import { NativeModules } from 'react-native'; -import { InitializationMode } from '../../../DdSdkReactNativeConfiguration'; import { DdSdkReactNative } from '../../../DdSdkReactNative'; +import { InitializationMode } from '../../../config/types'; import { DdRumUserInteractionTracking } from '../../../rum/instrumentation/interactionTracking/DdRumUserInteractionTracking'; import { XMLHttpRequestMock } from '../../../rum/instrumentation/resourceTracking/__tests__/__utils__/XMLHttpRequestMock'; +import { PropagatorType } from '../../../rum/types'; import { DefaultTimeProvider } from '../../../utils/time-provider/DefaultTimeProvider'; import { GlobalState } from '../../GlobalState/GlobalState'; import { BufferSingleton } from '../Buffer/BufferSingleton'; @@ -143,12 +144,19 @@ describe('DatadogProvider', () => { rumConfiguration: { trackErrors: true, trackResources: true, - trackInteractions: true - }, - firstPartyHosts: ['api.com'], - traceConfiguration: { - resourceTraceSampleRate: 100 + trackInteractions: true, + resourceTraceSampleRate: 100, + firstPartyHosts: [ + { + match: 'api.com', + propagatorTypes: [ + PropagatorType.DATADOG, + PropagatorType.TRACECONTEXT + ] + } + ] }, + traceConfiguration: {}, logsConfiguration: {} } }); @@ -177,7 +185,8 @@ describe('DatadogProvider', () => { expect(NativeModules.DdSdk.initialize).toHaveBeenCalledTimes(1); expect( - NativeModules.DdSdk.initialize.mock.calls[0][0].firstPartyHosts + NativeModules.DdSdk.initialize.mock.calls[0][0].rumConfiguration + .firstPartyHosts ).toEqual([ { match: 'api.com', diff --git a/packages/core/src/sdk/DdSdkInternal.ts b/packages/core/src/sdk/DdSdkInternal.ts index b483f544c..ad6e57af0 100644 --- a/packages/core/src/sdk/DdSdkInternal.ts +++ b/packages/core/src/sdk/DdSdkInternal.ts @@ -4,8 +4,8 @@ * Copyright 2016-Present Datadog, Inc. */ +import type { DdSdkNativeConfiguration } from '../config/features/CoreConfigurationNative'; import type { DdNativeSdkType } from '../nativeModulesTypes'; -import type { DdSdkNativeConfiguration } from '../types'; import type { AttributeEncoder } from './AttributesEncoding/types'; diff --git a/packages/core/src/sdk/EventMappers/EventMapper.ts b/packages/core/src/sdk/EventMappers/EventMapper.ts index 0f3878fe1..1d8adfa1f 100644 --- a/packages/core/src/sdk/EventMappers/EventMapper.ts +++ b/packages/core/src/sdk/EventMappers/EventMapper.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types/SdkVerbosity'; import { AccountInfoSingleton } from '../AccountInfoSingleton/AccountInfoSingleton'; import type { AccountInfo } from '../AccountInfoSingleton/types'; import { AttributesSingleton } from '../AttributesSingleton/AttributesSingleton'; diff --git a/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts b/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts deleted file mode 100644 index 8de97c1b1..000000000 --- a/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -import type { - BatchProcessingLevel, - FirstPartyHostsConfiguration, - UploadFrequency, - VitalsUpdateFrequency -} from '../../DdSdkReactNativeConfiguration'; -import { - DEFAULTS, - DatadogProviderConfiguration, - RumConfiguration, - TraceConfiguration, - LogsConfiguration -} from '../../DdSdkReactNativeConfiguration'; -import type { ProxyConfiguration } from '../../ProxyConfiguration'; -import { SdkVerbosity } from '../../SdkVerbosity'; -import { TrackingConsent } from '../../TrackingConsent'; -import type { ActionEventMapper } from '../../rum/eventMappers/actionEventMapper'; -import type { ErrorEventMapper } from '../../rum/eventMappers/errorEventMapper'; -import type { ResourceEventMapper } from '../../rum/eventMappers/resourceEventMapper'; -import { PropagatorType } from '../../rum/types'; - -export class FileBasedConfiguration extends DatadogProviderConfiguration { - constructor(params?: { - configuration?: unknown; - errorEventMapper?: ErrorEventMapper; - resourceEventMapper?: ResourceEventMapper; - actionEventMapper?: ActionEventMapper; - }) { - const configuration = getJSONConfiguration(params?.configuration); - super( - configuration.clientToken, - configuration.env, - configuration.trackingConsent, - configuration.useAccessibilityLabel - ); - - this.verbosity = configuration.verbosity; - this.site = configuration.site || DEFAULTS.site; - this.firstPartyHosts = - configuration.firstPartyHosts || DEFAULTS.getFirstPartyHosts(); - - if (configuration.rumConfiguration) { - const rumConfig = new RumConfiguration( - configuration.rumConfiguration.applicationId, - configuration.rumConfiguration.trackInteractions, - configuration.rumConfiguration.trackResources, - configuration.rumConfiguration.trackErrors - ); - - rumConfig.longTaskThresholdMs = - configuration.rumConfiguration.longTaskThresholdMs ?? - DEFAULTS.longTaskThresholdMs; - - if (configuration.rumConfiguration.actionNameAttribute) { - rumConfig.actionNameAttribute = - configuration.rumConfiguration.actionNameAttribute; - } - - rumConfig.errorEventMapper = - params?.errorEventMapper || DEFAULTS.errorEventMapper; - rumConfig.resourceEventMapper = - params?.resourceEventMapper || DEFAULTS.resourceEventMapper; - rumConfig.actionEventMapper = - params?.actionEventMapper || DEFAULTS.actionEventMapper; - - this.rumConfiguration = rumConfig; - } - - if (configuration.logsConfiguration) { - this.logsConfiguration = new LogsConfiguration(); - this.logsConfiguration.bundleLogsWithRum = - configuration.logsConfiguration.bundleLogsWithRum ?? - DEFAULTS.bundleLogsWithRum; - this.logsConfiguration.bundleLogsWithTraces = - configuration.logsConfiguration.bundleLogsWithTraces ?? - DEFAULTS.bundleLogsWithTraces; - } - - if (configuration.traceConfiguration) { - this.traceConfiguration = new TraceConfiguration(); - this.traceConfiguration.resourceTraceSampleRate = - configuration.traceConfiguration.resourceTraceSampleRate || - DEFAULTS.resourceTraceSampleRate; - } - } -} - -const resolveJSONConfiguration = ( - userSpecifiedConfiguration: unknown -): Record => { - if (typeof userSpecifiedConfiguration !== 'object') { - console.error(`Failed to parse the Datadog configuration file you provided. -Your configuration must validate the node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json JSON schema. -You can use VSCode to check your configuration by adding the following line to your JSON file: -{ - "$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json", -}`); - - return {}; - } - - return (userSpecifiedConfiguration as any) as Record; -}; - -export const getJSONConfiguration = ( - userSpecifiedConfiguration: unknown -): { - clientToken: string; - env: string; - trackingConsent?: TrackingConsent; - verbosity?: SdkVerbosity; - service?: string; - useAccessibilityLabel?: boolean; - site?: string; - batchSize?: string; - batchProcessingLevel?: BatchProcessingLevel; - nativeCrashReportEnabled?: boolean; - nativeLongTaskThresholdMs?: number | false; - proxyConfiguration?: ProxyConfiguration; - uploadFrequency?: UploadFrequency; - version?: string; - versionSuffix?: string; - firstPartyHosts?: FirstPartyHostsConfiguration; - rumConfiguration?: { - applicationId: string; - trackInteractions?: boolean; - trackResources?: boolean; - trackErrors?: boolean; - longTaskThresholdMs?: number; - actionNameAttribute?: string; - appHangThreshold?: number; - initialResourceThreshold?: number; - trackMemoryWarnings?: boolean; - nativeViewTracking?: boolean; - nativeInteractionTracking?: boolean; - customEndpoint?: string; - sessionSampleRate?: number; - trackBackgroundEvents?: boolean; - trackFrustrations?: boolean; - trackNonFatalAnrs?: boolean; - trackWatchdogTerminations?: boolean; - vitalsUpdateFrequency?: VitalsUpdateFrequency; - }; - traceConfiguration?: { - resourceTraceSampleRate?: number; - customEndpoint?: string; - }; - logsConfiguration?: { - bundleLogsWithRum?: boolean; - bundleLogsWithTraces?: boolean; - customEndpoint?: string; - }; -} => { - const configuration = resolveJSONConfiguration(userSpecifiedConfiguration); - - if ( - configuration.clientToken === undefined || - configuration.env === undefined || - (configuration.rumConfiguration !== undefined && - configuration.rumConfiguration.applicationId === undefined) - ) { - console.warn( - 'DATADOG: Warning: Malformed json configuration file - clientToken and env are mandatory Core SDK properties. ApplicationId is mandatory to enable RUM.' - ); - } - - return { - clientToken: configuration.clientToken, - env: configuration.env, - trackingConsent: buildTrackingConsent(configuration.trackingConsent), - verbosity: buildSdkVerbosity(configuration.verbosity), - useAccessibilityLabel: configuration.useAccessibilityLabel, - site: configuration.site, - service: configuration.service, - version: configuration.version, - versionSuffix: configuration.versionSuffix, - batchSize: configuration.batchSize, - batchProcessingLevel: configuration.batchProcessingLevel, - uploadFrequency: configuration.uploadFrequency, - nativeLongTaskThresholdMs: configuration.nativeLongTaskThresholdMs, - nativeCrashReportEnabled: configuration.nativeCrashReportEnabled, - proxyConfiguration: configuration.proxyConfiguration, - firstPartyHosts: - buildFirstPartyHosts(configuration.firstPartyHosts) || - DEFAULTS.getFirstPartyHosts(), - ...(configuration.rumConfiguration !== undefined && { - rumConfiguration: { - applicationId: configuration.rumConfiguration.applicationId, - trackInteractions: - configuration.rumConfiguration.trackInteractions, - trackResources: configuration.rumConfiguration.trackResources, - trackErrors: configuration.rumConfiguration.trackErrors, - longTaskThresholdMs: - configuration.rumConfiguration.longTaskThresholdMs, - actionNameAttribute: - configuration.rumConfiguration.actionNameAttribute, - customEndpoint: configuration.rumConfiguration.customEndpoint, - sessionSampleRate: - configuration.rumConfiguration.sessionSampleRate, - trackBackgroundEvents: - configuration.rumConfiguration.trackBackgroundEvents, - trackFrustrations: - configuration.rumConfiguration.trackFrustrations, - trackNonFatalAnrs: - configuration.rumConfiguration.trackNonFatalAnrs, - trackWatchdogTerminations: - configuration.rumConfiguration.trackWatchdogTerminations, - vitalsUpdateFrequency: - configuration.rumConfiguration.vitalsUpdateFrequency - } - }), - ...(configuration.traceConfiguration !== undefined && { - traceConfiguration: { - resourceTraceSampleRate: - configuration.traceConfiguration.resourceTraceSampleRate, - customEndpoint: configuration.traceConfiguration.customEndpoint - } - }), - ...(configuration.logsConfiguration !== undefined && { - logsConfiguration: { - bundleLogsWithRum: - configuration.logsConfiguration.bundleLogsWithRum, - bundleLogsWithTraces: - configuration.logsConfiguration.bundleLogsWithTraces, - customEndpoint: configuration.logsConfiguration.customEndpoint - } - }) - }; -}; - -const buildFirstPartyHosts = ( - firstPartyHosts: { match: string; propagatorTypes: string[] }[] | undefined -): FirstPartyHostsConfiguration | undefined => { - if (!firstPartyHosts) { - return undefined; - } - - try { - return firstPartyHosts.map(({ match, propagatorTypes }) => ({ - match, - propagatorTypes: propagatorTypes.map(formatPropagatorType) - })); - } catch (error) { - console.error(`Failed to parse the first party hosts from the Datadog configuration file you provided: -${(error as any).message} -The first party hosts will not be set for this session. -`); - return undefined; - } -}; - -export const formatPropagatorType = ( - propagatorType: string -): PropagatorType => { - switch (propagatorType.toLowerCase()) { - case 'b3': { - return PropagatorType.B3; - } - case 'b3multi': { - return PropagatorType.B3MULTI; - } - case 'datadog': { - return PropagatorType.DATADOG; - } - case 'tracecontext': { - return PropagatorType.TRACECONTEXT; - } - default: { - throw new Error( - `Failed to parse propagator type ${propagatorType}.` - ); - } - } -}; - -const buildTrackingConsent = ( - trackingConsent: string | undefined -): TrackingConsent => { - if (trackingConsent === undefined) { - return DEFAULTS.trackingConsent; - } - - switch (trackingConsent.toLowerCase()) { - case 'granted': { - return TrackingConsent.GRANTED; - } - case 'pending': { - return TrackingConsent.PENDING; - } - case 'not_granted': { - return TrackingConsent.NOT_GRANTED; - } - default: { - return DEFAULTS.trackingConsent; - } - } -}; - -const buildSdkVerbosity = ( - verbosity: string | undefined -): SdkVerbosity | undefined => { - if (verbosity === undefined) { - return undefined; - } - switch (verbosity.toLowerCase()) { - case 'debug': { - return SdkVerbosity.DEBUG; - } - case 'info': { - return SdkVerbosity.INFO; - } - case 'warn': { - return SdkVerbosity.WARN; - } - case 'error': { - return SdkVerbosity.ERROR; - } - default: { - return undefined; - } - } -}; diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/configuration-all-fields.json b/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/configuration-all-fields.json deleted file mode 100644 index 6322c3c7d..000000000 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/configuration-all-fields.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "../../../../../datadog-configuration.schema.json", - "configuration": { - "env": "fake-env", - "clientToken": "fake-client-token", - "rumConfiguration": { - "applicationId": "fake-app-id", - "trackInteractions": true, - "trackResources": true, - "trackErrors": true, - "longTaskThresholdMs": 44, - "actionNameAttribute": "action-name-attr" - }, - "traceConfiguration": { - "resourceTraceSampleRate": 33 - }, - "logsConfiguration": {}, - "trackingConsent": "NOT_GRANTED", - "verbosity": "WARN", - "useAccessibilityLabel": false, - "site": "US5", - "firstPartyHosts": [ - { - "match": "example.com", - "propagatorTypes": [ - "B3MULTI", - "TRACECONTEXT" - ] - } - ] - } -} diff --git a/packages/core/src/trace/DdTrace.ts b/packages/core/src/trace/DdTrace.ts index 8c57a6ed2..177b12b5e 100644 --- a/packages/core/src/trace/DdTrace.ts +++ b/packages/core/src/trace/DdTrace.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../InternalLog'; -import { SdkVerbosity } from '../SdkVerbosity'; +import { SdkVerbosity } from '../config/types/SdkVerbosity'; import type { DdNativeTraceType } from '../nativeModulesTypes'; import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; import { diff --git a/packages/core/src/trace/__tests__/DdTrace.test.ts b/packages/core/src/trace/__tests__/DdTrace.test.ts index ba7a87d26..660d2725c 100644 --- a/packages/core/src/trace/__tests__/DdTrace.test.ts +++ b/packages/core/src/trace/__tests__/DdTrace.test.ts @@ -8,7 +8,7 @@ import { NativeModules } from 'react-native'; import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; +import { SdkVerbosity } from '../../config/types'; import { BufferSingleton } from '../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { DdTrace } from '../DdTrace'; diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 20464838b..4d3fda013 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -4,89 +4,13 @@ * Copyright 2016-Present Datadog, Inc. */ -import type { BatchProcessingLevel } from './DdSdkReactNativeConfiguration'; -import type { AttributeEncoder } from './sdk/AttributesEncoding/types'; +import type { DdSdkNativeConfiguration } from './config/features/CoreConfigurationNative'; declare global { // eslint-disable-next-line no-var, vars-on-top var __DD_RN_BABEL_PLUGIN_ENABLED__: boolean; } -/** - * A configuration object to initialize Datadog's features. - */ - -export class DdSdkNativeConfiguration { - constructor( - readonly additionalConfiguration: object, - readonly clientToken: string, - readonly env: string, - readonly site: string, - readonly service: string | undefined, - readonly verbosity: string | undefined, - readonly nativeCrashReportEnabled: boolean, - readonly nativeLongTaskThresholdMs: number, - readonly trackingConsent: string, - readonly uploadFrequency: string, - readonly batchSize: string, - readonly batchProcessingLevel: BatchProcessingLevel, - readonly proxyConfiguration: - | { - type: string; - address: string; - port: number; - username?: string; - password?: string; - } - | undefined, - readonly firstPartyHosts: { - match: string; - propagatorTypes: string[]; - }[], - readonly attributeEncoders: AttributeEncoder[], - readonly rumConfiguration: RUMNativeConfiguration | undefined, - readonly logsConfiguration: LogsNativeConfiguration | undefined, - readonly traceConfiguration: TraceNativeConfiguration | undefined, - readonly configurationForTelemetry: { - initializationType: string; - trackErrors: boolean; - trackInteractions: boolean; - trackNetworkRequests: boolean; - reactVersion: string; - reactNativeVersion: string; - } // eslint-disable-next-line no-empty-function - ) {} -} - -export type RUMNativeConfiguration = { - readonly applicationId: string; - readonly trackFrustrations: boolean; - readonly longTaskThresholdMs: number; - readonly sessionSampleRate: number; - readonly vitalsUpdateFrequency: string; - readonly trackBackgroundEvents: boolean; - readonly nativeViewTracking: boolean; - readonly nativeInteractionTracking: boolean; - readonly trackNonFatalAnrs: boolean | undefined; - readonly appHangThreshold: number | undefined; - readonly trackWatchdogTerminations: boolean | undefined; - readonly initialResourceThreshold: number | undefined; - readonly trackMemoryWarnings: boolean; - readonly telemetrySampleRate: number; - readonly customEndpoint: string; -}; - -export type LogsNativeConfiguration = { - readonly bundleLogsWithRum: boolean; - readonly bundleLogsWithTraces: boolean; - readonly customEndpoint: string; -}; - -export type TraceNativeConfiguration = { - readonly resourceTraceSampleRate: number; - readonly customEndpoint: string; -}; - /** * The entry point to initialize Datadog's features. */ diff --git a/packages/core/src/utils/longTasksUtils.ts b/packages/core/src/utils/longTasksUtils.ts index f394f8a19..c32b48044 100644 --- a/packages/core/src/utils/longTasksUtils.ts +++ b/packages/core/src/utils/longTasksUtils.ts @@ -13,9 +13,7 @@ const MAX_LONG_TASK_THRESHOLD_MS = 5000; * Also makes sure it is a number, passing `0` if it is `false`, as the React Native * bridge cannot handle values with dual types. */ -export const adaptLongTaskThreshold = ( - longTaskThreshold: number | false -): number => { +export const adaptLongTaskThreshold = (longTaskThreshold: number): number => { if (!longTaskThreshold) { return 0; }