Skip to content

Commit a6dc383

Browse files
authored
Merge pull request #941 from DataDog/carlosnogueira/RUM-9145/refactor-rum-action-tracking
[RUM 9145] Refactor RUM Action Tracking
2 parents 4aa4a34 + fa95a01 commit a6dc383

33 files changed

+3232
-43
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ This repository contains 2 main projects:
2323
* `codepush`: an integration for the [react-native-code-push](https://github.com/microsoft/react-native-code-push) library.
2424
* `core`: the core React Native SDK allowing tracking of logs, spans and RUM events.
2525
* `react-native-apollo-client`: an integration for the [Apollo Client](https://www.apollographql.com/docs/react/integrations/react-native/) library.
26+
* `react-native-babel-plugin`: a Babel plugin that enriches React Native components with contextual metadata.
2627
* `react-native-navigation`: an integration for the [react-native-navigation](https://github.com/wix/react-native-navigation) library.
28+
* `react-native-session-replay`: client-side React Native module to enable session replay with Datadog.
2729
* `react-native-webview`: an integration for the [`react-native-webview`](https://github.com/react-native-webview/react-native-webview) library.
2830
* `react-navigation`: an integration for the [react-navigation](https://github.com/react-navigation/react-navigation) library.
2931
* Sample app project (in the `example` folder)

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"url": "https://github.com/DataDog/dd-sdk-reactnative/issues"
1515
},
1616
"license": "Apache-2.0",
17-
"private": true,
17+
"publishConfig": {
18+
"access": "public"
19+
},
1820
"workspaces": {
1921
"packages": [
2022
"packages/*",

packages/core/__mocks__/react-native.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ actualRN.NativeModules.DdSdk = {
3333
setTrackingConsent: jest.fn().mockImplementation(
3434
() => new Promise<void>(resolve => resolve())
3535
) as jest.MockedFunction<DdNativeSdkType['setTrackingConsent']>,
36+
sendTelemetryLog: jest.fn().mockImplementation(
37+
() => new Promise<void>(resolve => resolve())
38+
) as jest.MockedFunction<DdNativeSdkType['sendTelemetryLog']>,
3639
telemetryDebug: jest.fn().mockImplementation(
3740
() => new Promise<void>(resolve => resolve())
3841
) as jest.MockedFunction<DdNativeSdkType['telemetryDebug']>,

packages/core/jest/mock.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ module.exports = {
4242
setTrackingConsent: jest
4343
.fn()
4444
.mockImplementation(() => new Promise(resolve => resolve())),
45+
sendTelemetryLog: jest
46+
.fn()
47+
.mockImplementation(() => new Promise(resolve => resolve())),
4548
telemetryDebug: jest
4649
.fn()
4750
.mockImplementation(() => new Promise(resolve => resolve())),

packages/core/src/DdSdkReactNative.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,27 @@
77
import { version as reactNativeVersion } from 'react-native/package.json';
88
import { InteractionManager } from 'react-native';
99

10+
import type {
11+
AutoInstrumentationConfiguration,
12+
AutoInstrumentationParameters,
13+
DatadogProviderConfiguration,
14+
InitializationModeForTelemetry,
15+
PartialInitializationConfiguration
16+
} from './DdSdkReactNativeConfiguration';
1017
import {
1118
DdSdkReactNativeConfiguration,
12-
buildConfigurationFromPartialConfiguration,
13-
addDefaultValuesToAutoInstrumentationConfiguration,
1419
InitializationMode,
20+
addDefaultValuesToAutoInstrumentationConfiguration,
21+
buildConfigurationFromPartialConfiguration,
1522
formatFirstPartyHosts
1623
} from './DdSdkReactNativeConfiguration';
17-
import type {
18-
AutoInstrumentationParameters,
19-
DatadogProviderConfiguration,
20-
PartialInitializationConfiguration,
21-
AutoInstrumentationConfiguration,
22-
InitializationModeForTelemetry
23-
} from './DdSdkReactNativeConfiguration';
2424
import { InternalLog } from './InternalLog';
2525
import { SdkVerbosity } from './SdkVerbosity';
2626
import type { TrackingConsent } from './TrackingConsent';
2727
import { DdLogs } from './logs/DdLogs';
2828
import { DdRum } from './rum/DdRum';
2929
import { DdRumErrorTracking } from './rum/instrumentation/DdRumErrorTracking';
30+
import { DdBabelInteractionTracking } from './rum/instrumentation/interactionTracking/DdBabelInteractionTracking';
3031
import { DdRumUserInteractionTracking } from './rum/instrumentation/interactionTracking/DdRumUserInteractionTracking';
3132
import { DdRumResourceTracking } from './rum/instrumentation/resourceTracking/DdRumResourceTracking';
3233
import { AttributesSingleton } from './sdk/AttributesSingleton/AttributesSingleton';
@@ -361,6 +362,13 @@ export class DdSdkReactNative {
361362
private static enableFeatures(
362363
configuration: AutoInstrumentationParameters
363364
) {
365+
if (globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__) {
366+
DdBabelInteractionTracking.config = {
367+
trackInteractions: configuration.trackInteractions,
368+
useAccessibilityLabel: configuration.useAccessibilityLabel
369+
};
370+
}
371+
364372
if (DdSdkReactNative.wasAutoInstrumented) {
365373
InternalLog.log(
366374
"Can't auto instrument Datadog, SDK was already instrumented",
@@ -369,7 +377,10 @@ export class DdSdkReactNative {
369377
return;
370378
}
371379

372-
if (configuration.trackInteractions) {
380+
if (
381+
configuration.trackInteractions &&
382+
!globalThis.__DD_RN_BABEL_PLUGIN_ENABLED__
383+
) {
373384
DdRumUserInteractionTracking.startTracking({
374385
actionNameAttribute: configuration.actionNameAttribute,
375386
useAccessibilityLabel: configuration.useAccessibilityLabel

packages/core/src/index.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
* Copyright 2016-Present Datadog, Inc.
55
*/
66
import {
7+
BatchProcessingLevel,
8+
BatchSize,
79
DatadogProviderConfiguration,
810
DdSdkReactNativeConfiguration,
911
InitializationMode,
10-
VitalsUpdateFrequency,
1112
UploadFrequency,
12-
BatchSize,
13-
BatchProcessingLevel
13+
VitalsUpdateFrequency
1414
} from './DdSdkReactNativeConfiguration';
1515
import type {
1616
AutoInstrumentationConfiguration,
@@ -23,26 +23,27 @@ import { SdkVerbosity } from './SdkVerbosity';
2323
import { TrackingConsent } from './TrackingConsent';
2424
import { DdLogs } from './logs/DdLogs';
2525
import { DdRum } from './rum/DdRum';
26+
import { DdBabelInteractionTracking } from './rum/instrumentation/interactionTracking/DdBabelInteractionTracking';
2627
import { DatadogTracingContext } from './rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingContext';
2728
import { DatadogTracingIdentifier } from './rum/instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier';
2829
import {
29-
TracingIdType,
30-
TracingIdFormat
30+
TracingIdFormat,
31+
TracingIdType
3132
} from './rum/instrumentation/resourceTracking/distributedTracing/TracingIdentifier';
3233
import {
33-
DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER,
3434
DATADOG_GRAPH_QL_OPERATION_NAME_HEADER,
35+
DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER,
3536
DATADOG_GRAPH_QL_VARIABLES_HEADER
3637
} from './rum/instrumentation/resourceTracking/graphql/graphqlHeaders';
37-
import { RumActionType, ErrorSource, PropagatorType } from './rum/types';
3838
import type { FirstPartyHost } from './rum/types';
39+
import { ErrorSource, PropagatorType, RumActionType } from './rum/types';
3940
import { DatadogProvider } from './sdk/DatadogProvider/DatadogProvider';
4041
import { DdSdk } from './sdk/DdSdk';
4142
import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration';
4243
import { DdTrace } from './trace/DdTrace';
4344
import { DefaultTimeProvider } from './utils/time-provider/DefaultTimeProvider';
44-
import { TimeProvider } from './utils/time-provider/TimeProvider';
4545
import type { Timestamp } from './utils/time-provider/TimeProvider';
46+
import { TimeProvider } from './utils/time-provider/TimeProvider';
4647

4748
export {
4849
DatadogProvider,
@@ -75,9 +76,9 @@ export {
7576
TracingIdType,
7677
TracingIdFormat,
7778
DatadogTracingIdentifier,
78-
DatadogTracingContext
79+
DatadogTracingContext,
80+
DdBabelInteractionTracking
7981
};
80-
8182
export type {
8283
Timestamp,
8384
FirstPartyHost,

packages/core/src/rum/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
export const BABEL_PLUGIN_TELEMETRY = 'Datadog Babel Integration Telemetry';
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
import { InternalLog } from '../../../InternalLog';
8+
import { SdkVerbosity } from '../../../SdkVerbosity';
9+
import DdNativeRum from '../../../specs/NativeDdRum';
10+
import DdSdk from '../../../specs/NativeDdSdk';
11+
import { getBabelTelemetryConfig } from '../../../utils/telemetry';
12+
import { DefaultTimeProvider } from '../../../utils/time-provider/DefaultTimeProvider';
13+
import type { TimeProvider } from '../../../utils/time-provider/TimeProvider';
14+
import { BABEL_PLUGIN_TELEMETRY } from '../../constants';
15+
import type { RumActionType } from '../../types';
16+
import { ActionSource } from '../../types';
17+
18+
const StateErrors = {
19+
ALREADY_INITIALIZED:
20+
'Interaction Tracking singleton already initialized, please use `getInstance`.'
21+
} as const;
22+
23+
type BabelConfig = {
24+
trackInteractions: boolean;
25+
useAccessibilityLabel: boolean;
26+
};
27+
28+
type TargetObject = {
29+
compoenentName: string;
30+
'dd-action-name': string;
31+
accessibilityLabel: string;
32+
[key: string]: string;
33+
};
34+
35+
export class DdBabelInteractionTracking {
36+
private static instance: DdBabelInteractionTracking | null = null;
37+
38+
static config: BabelConfig = {
39+
trackInteractions: false,
40+
useAccessibilityLabel: true
41+
};
42+
43+
private timeProvider: TimeProvider = new DefaultTimeProvider();
44+
45+
private telemetrySent: boolean = false;
46+
47+
isInitialized: boolean = false;
48+
49+
private constructor() {
50+
if (DdBabelInteractionTracking.instance) {
51+
throw new Error(StateErrors.ALREADY_INITIALIZED);
52+
}
53+
54+
DdBabelInteractionTracking.instance = this;
55+
}
56+
57+
static getInstance() {
58+
if (!DdBabelInteractionTracking.instance) {
59+
DdBabelInteractionTracking.instance = new DdBabelInteractionTracking();
60+
}
61+
62+
return DdBabelInteractionTracking.instance;
63+
}
64+
65+
private getTargetName(targetObject: TargetObject) {
66+
const {
67+
componentName,
68+
'dd-action-name': actionName,
69+
accessibilityLabel,
70+
...attrs
71+
} = targetObject;
72+
73+
const { useAccessibilityLabel } = DdBabelInteractionTracking.config;
74+
75+
if (actionName) {
76+
return actionName;
77+
}
78+
79+
const keys = Object.keys(attrs);
80+
if (keys.length) {
81+
return attrs[keys[0]];
82+
}
83+
84+
if (useAccessibilityLabel && accessibilityLabel) {
85+
return accessibilityLabel;
86+
}
87+
88+
return componentName;
89+
}
90+
91+
wrapRumAction(
92+
func: (...args: any[]) => any,
93+
action: RumActionType,
94+
targetObject: TargetObject
95+
): (...args: any[]) => any {
96+
return (...args: any[]) => {
97+
const result = func(...args);
98+
99+
if (!this.telemetrySent) {
100+
DdSdk?.sendTelemetryLog(
101+
BABEL_PLUGIN_TELEMETRY,
102+
getBabelTelemetryConfig(),
103+
{ onlyOnce: true }
104+
);
105+
106+
this.telemetrySent = true;
107+
}
108+
109+
const targetName = this.getTargetName(targetObject);
110+
111+
const { trackInteractions } = DdBabelInteractionTracking.config;
112+
113+
if (trackInteractions) {
114+
InternalLog.log(
115+
`Adding RUM Action “${targetName}” (${action}, auto)`,
116+
SdkVerbosity.DEBUG
117+
);
118+
119+
DdNativeRum?.addAction(
120+
action,
121+
targetName,
122+
{ '__dd.action_source': ActionSource.BABEL },
123+
this.timeProvider.now()
124+
);
125+
}
126+
127+
return result;
128+
};
129+
}
130+
}

packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import { InternalLog } from '../../../InternalLog';
1010
import { SdkVerbosity } from '../../../SdkVerbosity';
1111
import { DdSdk } from '../../../sdk/DdSdk';
1212
import { getErrorMessage } from '../../../utils/errorUtils';
13+
import { getBabelTelemetryConfig } from '../../../utils/telemetry';
14+
import { BABEL_PLUGIN_TELEMETRY } from '../../constants';
1315

14-
import { DdEventsInterceptor } from './DdEventsInterceptor';
1516
import type { DdEventsInterceptorOptions } from './DdEventsInterceptor';
17+
import { DdEventsInterceptor } from './DdEventsInterceptor';
1618
import type { EventsInterceptor } from './EventsInterceptor';
1719
import { NoOpEventsInterceptor } from './NoOpEventsInterceptor';
1820
import { areObjectShallowEqual } from './ShallowObjectEqualityChecker';
@@ -70,6 +72,12 @@ export class DdRumUserInteractionTracking {
7072
return;
7173
}
7274

75+
DdSdk?.sendTelemetryLog(
76+
BABEL_PLUGIN_TELEMETRY,
77+
getBabelTelemetryConfig(),
78+
{ onlyOnce: true }
79+
);
80+
7381
DdRumUserInteractionTracking.eventsInterceptor = new DdEventsInterceptor(
7482
options
7583
);

packages/core/src/rum/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@ export enum PropagatorType {
255255
B3MULTI = 'b3multi'
256256
}
257257

258+
export enum ActionSource {
259+
MANUAL = 'MANUAL',
260+
LEGACY = 'LEGACY',
261+
BABEL = 'BABEL'
262+
}
263+
258264
export type FirstPartyHost = {
259265
match: string;
260266
propagatorTypes: PropagatorType[];

0 commit comments

Comments
 (0)