@dynatrace/react-native-plugin 2.343.1 → 2.345.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +638 -660
  2. package/android/build.gradle +6 -2
  3. package/files/plugin-runtime.gradle +17 -33
  4. package/files/plugin.gradle +1 -1
  5. package/instrumentation/BabelPluginDynatrace.js +1 -1
  6. package/instrumentation/libs/UserInteraction.js +20 -6
  7. package/instrumentation/libs/react-navigation/ReactNavigation.js +14 -58
  8. package/instrumentation/libs/withOnPressMonitoring.js +3 -54
  9. package/internal.d.ts +1 -0
  10. package/internal.js +3 -1
  11. package/ios/DynatraceRNBridge.mm +9 -0
  12. package/lib/core/configuration/ConfigurationHandler.js +0 -21
  13. package/lib/dynatrace-reporter.js +1 -30
  14. package/lib/dynatrace-transformer.js +6 -34
  15. package/lib/next/configuration/INativeRuntimeConfiguration.js +0 -1
  16. package/lib/next/configuration/RuntimeConfigurationObserver.js +1 -6
  17. package/lib/next/navigation/NavigationTracker.js +93 -0
  18. package/lib/next/navigation/useNavigationTracking.js +54 -0
  19. package/package.json +9 -21
  20. package/public.js +3 -1
  21. package/react-native-dynatrace.podspec +1 -1
  22. package/scripts/Android.js +1 -20
  23. package/scripts/Config.js +0 -4
  24. package/scripts/Deprecations.js +18 -0
  25. package/scripts/Install.js +13 -0
  26. package/scripts/PathsConstants.js +0 -4
  27. package/scripts/Uninstall.js +0 -15
  28. package/scripts/core/InstrumentCall.js +2 -3
  29. package/scripts/util/DeprecationReport.js +24 -0
  30. package/types.d.ts +212 -10
  31. package/instrumentation/DynatraceInstrumentation.js +0 -1
  32. package/instrumentation/libs/community/Picker.InstrInfo.js +0 -20
  33. package/instrumentation/libs/community/gesture-handler/Touchables.InstrInfo.js +0 -25
  34. package/instrumentation/libs/react-native/RefreshControl.InstrInfo.js +0 -12
  35. package/instrumentation/libs/react-native/Switch.InstrInfo.js +0 -12
  36. package/instrumentation/libs/react-native/Touchables.InstrInfo.js +0 -25
  37. package/instrumentation/model/Reference.js +0 -2
  38. package/instrumentation/parser/Babel.js +0 -60
  39. package/instrumentation/parser/ParserUtil.js +0 -11
  40. package/lib/features/ui-interaction/Config.js +0 -42
  41. package/lib/features/ui-interaction/IUserInteractionEvent.js +0 -16
  42. package/lib/features/ui-interaction/Plugin.Fragment.Test.js +0 -170
  43. package/lib/features/ui-interaction/Plugin.js +0 -289
  44. package/lib/features/ui-interaction/RootDetection.js +0 -51
  45. package/lib/features/ui-interaction/RootWrapper.js +0 -236
  46. package/lib/features/ui-interaction/Run.js +0 -38
  47. package/lib/features/ui-interaction/Runtime.js +0 -843
  48. package/lib/features/ui-interaction/TouchMetaResolver.js +0 -493
  49. package/lib/features/ui-interaction/Types.js +0 -14
  50. package/lib/metro/getSourceMapInfo.js +0 -26
  51. package/scripts/LineOffsetAnalyze.js +0 -25
  52. package/scripts/core/LineOffsetAnalyzeCall.js +0 -165
  53. package/scripts/util/SourceMapUtil.js +0 -68
package/README.md CHANGED
@@ -35,8 +35,8 @@ If you want to start using this plugin and are not a Dynatrace customer yet, hea
35
35
  ## Agent Versions
36
36
  These agent versions are configured in this plugin:
37
37
 
38
- * Android Agent: 8.343.1.1038
39
- * iOS Agent: 8.343.1.1007
38
+ * Android Agent: 8.345.1.1003
39
+ * iOS Agent: 8.345.1.1003
40
40
 
41
41
  ## Quick Setup
42
42
 
@@ -48,36 +48,35 @@ These agent versions are configured in this plugin:
48
48
 
49
49
  ## Advanced topics
50
50
  * [Manual OneAgent Startup](#manual-oneagent-startup)
51
- * [Manual instrumentation](#manual-instrumentation)
52
- * [Plugin Startup](#plugin-startup)
51
+ * [Real User Monitoring (RUM)](#real-user-monitoring-rum)
52
+ * [User Interaction](#user-interaction)
53
+ * [Send Event](#send-event)
54
+ * [Send Session Property Event](#send-session-property-event)
55
+ * [Event Modifier](#event-modifier)
56
+ * [Send Exception Event](#send-exception-event)
57
+ * [Send HTTP Request Event](#send-http-request-event)
58
+ * [View Monitoring](#view-monitoring)
59
+ * [React Native Symbolication](#react-native-symbolication)
60
+ * [RUM Classic](#rum-classic)
53
61
  * [Monitor a Component](#monitor-a-component)
54
62
  * [Create Custom Action](#create-custom-actions)
55
63
  * [Cancel Actions](#cancel-actions)
56
64
  * [Manual Web Request Tagging](#manual-web-request-tagging)
57
65
  * [Report Values](#report-values)
58
66
  * [Report Stacktrace](#report-an-error-stacktrace)
59
- * [Identify User](#identify-a-user)
60
- * [End Session](#end-the-current-user-session)
61
67
  * [Crash Reporting](#manually-report-a-crash)
62
- * [User Privacy Options](#user-privacy-options)
63
68
  * [Report GPS Position](#report-gps-location)
64
69
  * [Business event capturing](#business-event-capturing)
65
70
  * [Platform independent reporting](#platform-independent-reporting)
66
71
  * [Set beacon headers](#setting-beacon-headers)
67
72
  * [Exclude Individual JSX Elements](#exclude-individual-jsx-elements)
68
- * [New RUM experience](#new-rum-experience)
69
- * [User Interaction](#user-interaction)
70
- * [Send Event](#send-event)
71
- * [Send Session Property Event](#send-session-property-event)
72
- * [Event Modifier](#event-modifier)
73
- * [Send Exception Event](#send-exception-event)
74
- * [Send HTTP Request Event](#send-http-request-event)
75
- * [View Monitoring](#view-monitoring)
76
- * [React Native Symbolication](#react-native-symbolication)
73
+ * [Extended topics](#extended-topics)
74
+ * [Identify User](#identify-a-user)
75
+ * [End Session](#end-the-current-user-session)
76
+ * [User Privacy Options](#user-privacy-options)
77
77
  * [NPX Commands](#npx-commands)
78
78
  * [npx instrumentDynatrace](#npx-instrumentdynatrace)
79
79
  * [npx configDynatrace](#npx-configdynatrace)
80
- * [npx lineOffsetDynatrace](#npx-lineoffsetdynatrace)
81
80
  * [Customizing paths for configuration](#customizing-paths-for-configuration)
82
81
  * [Manual adding iOS Agent to project](#manually-adding-ios-oneagent-to-a-project)
83
82
  * [Setup for tvOS](#setup-for-tvos)
@@ -91,10 +90,8 @@ These agent versions are configured in this plugin:
91
90
  * [Autostart](#autostart)
92
91
  * [Bundle Name and Version](#bundle-name-and-version)
93
92
  * [Navigation](#navigation)
94
- * [Source Map](#source-map)
95
93
  * [User Interaction Configuration](#user-interaction-configuration)
96
94
  * [Debugging our auto-instrumentation](#debugging-our-auto-instrumentation)
97
- * [Using our legacy jscodeshift auto-instrumentation](#using-our-legacy-jscodeshift-auto-instrumentation)
98
95
  * [Android block](#android-block)
99
96
  * [iOS block](#ios-block)
100
97
  * [Lifecycle modes](#lifecycle)
@@ -221,31 +218,7 @@ module.exports = {
221
218
  };
222
219
  ```
223
220
 
224
- 2. Make the start-up call with at least `beaconUrl` and `applicationId`:
225
-
226
- Example of a startup call:
227
-
228
- ```ts
229
- import { Dynatrace, ConfigurationBuilder } from '@dynatrace/react-native-plugin';
230
-
231
- await Dynatrace.start(new ConfigurationBuilder("beaconUrl", "applicationId").buildConfiguration());
232
- ```
233
-
234
- For more details see the section about [startup API](#plugin-startup).
235
-
236
- **Note**: If you don't deactivate the automated startup with the `dynatrace.config.js` file, the `beaconUrl` and `applicationId` values have no impact and are thrown away.
237
-
238
- ## Manual instrumentation
239
-
240
- To use the API of the React Native plugin, import the API:
241
-
242
- ```ts
243
- import { Dynatrace } from '@dynatrace/react-native-plugin';
244
- ```
245
-
246
- ### Plugin startup
247
-
248
- The manual startup of the plugin is triggered via the `start(configuration: IConfiguration)` method. If you configured `dynatrace.config.js` for manual startup then the plugin doesn't send any data when not calling this function. Besides the application id and the beacon URL, there are several optional configuration parameters, which are shown in the table below.
221
+ 2. Call `Dynatrace.start()` with your configuration. The startup call is triggered via the `start(configuration: IConfiguration)` method if you configured `dynatrace.config.js` for manual startup, the plugin doesn't send any data until this function is called. At minimum you need `beaconUrl` and `applicationId`, but there are several optional configuration parameters as well:
249
222
 
250
223
  ```ts
251
224
  import { Dynatrace, ConfigurationBuilder, LogLevel } from '@dynatrace/react-native-plugin';
@@ -265,6 +238,8 @@ configurationBuilder.withCrashReporting(true)
265
238
  await Dynatrace.start(configurationBuilder.buildConfiguration());
266
239
  ```
267
240
 
241
+ **Note**: If you don't deactivate the automated startup with the `dynatrace.config.js` file, the `beaconUrl` and `applicationId` values have no impact and are thrown away.
242
+
268
243
  **Info**: The value used in the function calls for the parameters is also their default value.
269
244
 
270
245
  | Property name | Type | Default | Description |
@@ -282,866 +257,867 @@ await Dynatrace.start(configurationBuilder.buildConfiguration());
282
257
  |actionNameAlgorithm |string |'depth-first' |Controls the traversal algorithm used when searching the component tree for the name of an `onPress` action. `'depth-first'` follows the first child branch fully before trying siblings. `'breadth-first'` visits all siblings at a level before going deeper, returning the shallowest match first.|
283
258
  |bundleName |string |undefined |Should be used only if you have a multiple bundle setup where you load several .bundle files within your React Native application. Enter the name of your bundle. This should be unique in comparison to your other bundle names. This will ensure that actions coming from different bundles will not interfere with each other.
284
259
 
285
- **Attention:**
260
+ **Attention:**
286
261
  * Keep in mind that configuration within the `dynatrace.config.js` file is the basis, even for manual startup. When we look at the lifecycleUpdate property: Per default if not used, it is false. If enabled (set to true) in `dynatrace.config.js` file, this will be also true if manual startup is used. You can still override this behavior by calling `ConfigurationBuilder.withLifecycleUpdate(false)`.
287
262
  * Please use those parameters only when doing a manual startup. If you want to do an automated startup, please configure the properties via the [auto startup configuration](#manual-startup-counterparts). You will find a list which explains all the counterparts for the available options here.
288
263
 
289
- ### Monitor a Component
264
+ ## Real User Monitoring (RUM)
290
265
 
291
- A component can be either monitored automatically or manually. The auto instrumentation is handled via the dynatrace.config.js file. If you want to manually instrument a component you can use the API call `withMonitoring`.
266
+ Real User Monitoring introduces a set of advanced APIs that allow you to send custom events, modify event data, track exceptions, monitor HTTP requests, and manage view contexts in your React Native application. These APIs provide more granular control over the data captured by Dynatrace and are designed for the next generation RUM capabilities.
292
267
 
293
- * Example with Functional Component:
268
+ For more detailed information about Real User Monitoring, see the [official Dynatrace documentation](https://docs.dynatrace.com/docs/shortlink/react-native-main).
294
269
 
295
- ```ts
296
- import { Dynatrace } from '@dynatrace/react-native-plugin';
297
- import { FunctionComponent } from 'react';
270
+ ### Send Event
298
271
 
299
- export const MyFunctionalComponent: FunctionComponent<{}> = () => {
300
- return null;
301
- }
272
+ The `sendEvent()` method allows you to send custom events with arbitrary properties using the EventData class. This is useful for tracking specific user interactions or application state changes.
302
273
 
303
- Dynatrace.withMonitoring(MyFunctionalComponent, "MyFunctionalComponent");
274
+ ```ts
275
+ import { Dynatrace, EventData } from '@dynatrace/react-native-plugin';
276
+
277
+ // Send a custom event with properties
278
+ Dynatrace.sendEvent(new EventData()
279
+ .addEventProperty("event_properties.button_clicked", "login_button")
280
+ .addEventProperty("event_properties.user_type", "premium")
281
+ .addEventProperty("event_properties.attempt_count", 3)
282
+ .withDuration(250)
283
+ );
304
284
  ```
305
- ![rnFunctionalComp](https://dt-cdn.net/images/rnfunctionalcomp-800-d98eacef6d.png)
306
285
 
307
- The String "MyFunctionalComponent" is optional as the name of the component can be retrieved through [different properties](#how-does-dynatrace-determine-the-user-action-name).
286
+ **Property Requirements:**
287
+ * Only properties prefixed with `event_properties.*` are allowed
288
+ * Additionally, the `duration` property is allowed
289
+ * Maximum of 50 custom properties per event
290
+ * If the limit is exceeded, properties are sorted alphabetically by key and excess properties are dropped deterministically
291
+ * String properties are limited to 5000 characters (exceeding characters are truncated)
292
+ * Field names must contain only alphabetic characters, numbers, underscores, and dots
293
+ * Each dot must be followed by an alphabetic character
294
+ * Each underscore must be followed by an alphabetic character or number
295
+ * Values must be primitive types (string, number, boolean)
296
+ * Cannot contain functions, undefined, Infinity, or NaN as values (they will be replaced with null)
308
297
 
309
- Combining manual and auto instrumentation should not be a problem. As they're running the same execution, the manual instrumentation will only override the content of auto instrumentation happening through the transformer.
298
+ ### Send Session Property Event
310
299
 
311
- ### Create custom actions
300
+ Session properties apply to all events within the current session. Use `sendSessionPropertyEvent()` to set properties that should be available across the entire user session.
312
301
 
313
- There are two options to create an action. Either using `enterAutoAction` (the previous `enterAction`) or `enterManualAction`:
302
+ ```ts
303
+ import { Dynatrace, SessionPropertyEventData } from '@dynatrace/react-native-plugin';
314
304
 
315
- **Important:** Action names are limited to 250 characters and will be truncated if they exceed this limit.
305
+ // Set session-wide properties
306
+ Dynatrace.sendSessionPropertyEvent(new SessionPropertyEventData()
307
+ .addSessionProperty("session_properties.user_tier", "premium")
308
+ .addSessionProperty("session_properties.app_version", "2.1.0")
309
+ .addSessionProperty("session_properties.feature_flag_enabled", true)
310
+ );
311
+ ```
316
312
 
317
- * `enterAutoAction` - Creates an Action which will be automatically handled by the plugin (This is the type of action which is internally used by the plugin when monitoring components and touchables). This means that the plugin decides about the hierarchy of this action. If there is no open action, the following action will be a root action. All other actions created by this method, while a root action is open, will be automatically inserted as a child action. Furthermore the plugin will automatically link webrequest (if they are not tagged manually) to the open root action. Be aware that the timeout/wait time for sub actions or web requests cannot be modified on the Android side for React Native Auto actions. The timeout is fixed to a 1000ms.
313
+ **Important Notes:**
314
+ * Session properties persist throughout the entire session
315
+ * If you send the same property multiple times, only one value will be kept (first or last)
316
+ * Use session properties for data that applies to the entire user session
317
+ * Field naming follows the same rules as events, but with `session_properties.` prefix
318
+ * Maximum of 50 custom properties per event
319
+ * If the limit is exceeded, properties are sorted alphabetically by key and excess properties are dropped deterministically
318
320
 
319
- ```ts
320
- import { Dynatrace } from '@dynatrace/react-native-plugin';
321
+ ### Event Modifier
321
322
 
322
- const myAction = Dynatrace.enterAutoAction("MyButton tapped");
323
- //Perform the action and whatever else is needed.
324
- myAction.leaveAction();
325
- ```
326
- ![rnSingle](https://dt-cdn.net/images/rnsingle-800-e7e1b343ed.png)
323
+ Event modifiers allow you to intercept and modify events before they are sent to Dynatrace. This is useful for adding common properties, filtering sensitive data, or enriching events with additional context. Event modifiers apply to all event types, including custom events, session properties, exceptions, and HTTP events.
327
324
 
328
- * `enterManualAction` - Creates an Action which will NOT be handled by the plugin. This means that you have full control about the hierarchy of your actions. This function will create a root action for you, which has the ability to create child actions via `enterAction`. Be aware, because of the full manual approach the plugin will not link webrequest automatically. Webrequest have to be manually tagged by using the tag provided by the action via `getRequestTag`.
325
+ If multiple event modifiers are added, they are executed in the order they were added.
329
326
 
330
- ```ts
331
- import { Dynatrace } from '@dynatrace/react-native-plugin';
327
+ Most fields and namespaces can't be modified in any way (added, removed or overridden), while others are open for modification.
332
328
 
333
- const myAction = Dynatrace.enterManualAction("MyButton tapped");
334
- //Perform the action and whatever else is needed.
335
- myAction.leaveAction();
336
- ```
329
+ **Open for modification and can be added:**
330
+ - `event_properties.*`
331
+ - `session_properties.*`
337
332
 
338
- To create a custom action named `"MyButton tapped"`, use the following code. The *leaveAction* closes the action again. To report values for this action before closing, see [Report Values](#report-values).
333
+ `session_properties.*` are only allowed to be on a session property event.
334
+
335
+ **Open for modification only:**
336
+ - `url.full`
337
+ - `exception.stack_trace`
338
+
339
+ #### Example
339
340
 
340
341
  ```ts
341
- import { Dynatrace } from '@dynatrace/react-native-plugin';
342
+ import { Dynatrace, IEventModifier } from '@dynatrace/react-native-plugin';
342
343
 
343
- const myAction = Dynatrace.enterAutoAction("MyButton tapped");
344
- //Perform the action and whatever else is needed.
345
- myAction.leaveAction();
344
+ // Create an event modifier
345
+ const myModifier: IEventModifier = {
346
+ modifyEvent(event) {
347
+ // Add common properties to all events
348
+ event["event_properties.app_build"] = "1.2.3";
349
+ event["event_properties.environment"] = "production";
350
+
351
+ // Return null to discard the event entirely
352
+ if (event["event_properties.ignore"] === true) {
353
+ return null;
354
+ }
355
+
356
+ return event;
357
+ }
358
+ };
359
+
360
+ // Add the modifier
361
+ Dynatrace.addEventModifier(myModifier);
362
+
363
+ // Remove the modifier when no longer needed
364
+ Dynatrace.removeEventModifier(myModifier);
346
365
  ```
347
366
 
348
- ### Create custom sub actions
367
+ #### Important Considerations
349
368
 
350
- You can create a single custom action as well as sub actions. The `MyButton Sub Action` is automatically put under the `MyButton tapped`. As long as `MyButton tapped` is open, it gathers all the web requests.
369
+ - **Execution order**: If multiple event modifiers are added, they are executed in the order they were added
370
+ - **Returning null**: Returning `null` discards the event and prevents future modifier functions from being executed
371
+ - **Performance**: Event modifiers should be efficient as they are called for every event
372
+ - **Field naming**: Custom properties must follow the `event_properties.*` or `session_properties.*` prefix naming convention
373
+ - **Reserved fields**: Certain reserved fields and namespaces cannot be modified. Attempts to modify them will be ignored
374
+ - **Error handling**: If a modifier throws an exception, it will be logged but won't prevent other modifiers from executing
375
+ - **Primitive values**: Event fields can only contain primitive values (String, int, double, bool)
376
+ - **Invalid argument**: In case you pass an invalid argument to `addEventModifier`, we return a no-op placeholder modifier.
351
377
 
352
- **Important:** Action names are limited to 250 characters and will be truncated if they exceed this limit.
378
+ ### Send Exception Event
379
+
380
+ The `sendExceptionEvent()` method provides a structured way to report exceptions with additional context and custom properties using the ExceptionEventData class.
353
381
 
354
382
  ```ts
355
- import { Dynatrace } from '@dynatrace/react-native-plugin';
383
+ import { Dynatrace, ExceptionEventData } from '@dynatrace/react-native-plugin';
356
384
 
357
- const myAction = Dynatrace.enterManualAction("MyButton tapped");
358
- const mySubAction = myAction.enterAction("MyButton Sub Action");
359
- //Perform the action and whatever else is needed.
360
- mySubAction.leaveAction();
361
- myAction.leaveAction();
385
+ try {
386
+ // Code that may throw an error
387
+ throw new Error('Something went wrong');
388
+ } catch (error) {
389
+ if (error instanceof Error) {
390
+ Dynatrace.sendExceptionEvent(new ExceptionEventData(error)
391
+ .addEventProperty('event_properties.custom_key', 'custom_value')
392
+ .addEventProperty('event_properties.error_context', 'user_action')
393
+ );
394
+ }
395
+ }
362
396
  ```
363
- ![subAction](https://dt-cdn.net/images/subaction-800-172dbcca6c.png)
364
397
 
365
- ### Cancel actions
398
+ **Parameters:**
399
+ * `error`: The Error object containing exception information (required)
366
400
 
367
- Actions can be canceled. That means they will not be sent and discarded fully. This also means that any values and sub actions attached to the action will be removed.
401
+ ### Send HTTP Request Event
402
+
403
+ The `sendHttpRequestEvent()` method allows you to manually report HTTP request events with detailed information about the request and response.
368
404
 
369
405
  ```ts
370
- import { Dynatrace } from '@dynatrace/react-native-plugin';
406
+ import { Dynatrace, HttpRequestEventData } from '@dynatrace/react-native-plugin';
371
407
 
372
- const myAction = Dynatrace.enterAutoAction("MyButton tapped");
373
- // Action will be canceled
374
- myAction.cancel();
408
+ // Basic HTTP request event
409
+ const requestEventData = new HttpRequestEventData('https://api.example.com/users', 'GET');
410
+ Dynatrace.sendHttpRequestEvent(requestEventData);
375
411
 
376
- // Has no impact as the action is already canceled
377
- myAction.leaveAction();
412
+ // HTTP request with additional details
413
+ const detailedRequestEventData = new HttpRequestEventData('https://api.example.com/data', 'POST')
414
+ .withStatusCode(200)
415
+ .addEventProperty('event_properties.headers.content_type', 'application/json');
416
+ Dynatrace.sendHttpRequestEvent(detailedRequestEventData);
378
417
  ```
379
418
 
380
- ### Manual Web Request Tagging
419
+ **Parameters:**
420
+ * `url`: The URL of the HTTP request (required)
421
+ * `method`: The HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE') (required)
381
422
 
382
- You can manually tag and time your web requests. With the API shown below, you are able to manually capture the web requests of an http framework/library.
423
+ ### View Monitoring
383
424
 
384
- **Note:**
385
- Using this API will force the request to be added to the action that is manually created.
425
+ The view monitoring APIs allow you to track different screens or views in your application, providing context for all events happening within those views.
386
426
 
387
- ```ts
388
- import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-plugin';
427
+ There are two ways to monitor views:
428
+ 1. **Automatic view monitoring** - Enable navigation tracking in your configuration to automatically capture view changes through supported navigation libraries (enabled by default)
429
+ 2. **Manual view monitoring** - Use the `startView()` API to manually control when view contexts are created and updated
389
430
 
390
- const action = Dynatrace.enterManualAction('API Data Fetch');
391
- const url = 'https://api.example.com/data';
392
- const tag = await action.getRequestTag(url);
393
- const timing = new DynatraceWebRequestTiming(tag, url);
431
+ > **Important:** These approaches should not be mixed, as the outcome is unpredictable. Choose either automatic or manual view monitoring for your application.
394
432
 
395
- try {
396
- timing.startWebRequestTiming();
397
- const response = await fetch(url, {
398
- headers: {
399
- [timing.getRequestTagHeader()]: tag
400
- }
401
- });
402
- timing.stopWebRequestTiming(response.status, response.statusText);
403
- } catch (error) {
404
- if (error instanceof Error) {
405
- timing.stopWebRequestTiming(-1, error.message);
406
- } else {
407
- timing.stopWebRequestTiming(-1, (error as any).toString());
433
+ #### Automatic Navigation tracking
434
+
435
+ > **Note:** This feature only works with `@react-navigation` versions 5.x through 7.x.
436
+
437
+ The following is an example of how this feature can be configured in your `dynatrace.config.js` file. Note that this feature is enabled by default.
438
+
439
+ ```js
440
+ react: {
441
+ navigation: {
442
+ enabled: true
408
443
  }
409
- } finally {
410
- action.leaveAction();
411
444
  }
412
445
  ```
413
- ![rnManualWeb](https://dt-cdn.net/images/rnmanualweb-800-662d009613.png)
414
446
 
415
- There is also the option to report values for request and response size:
447
+ When this feature is enabled, the view context will represent the current state of the `@react-navigation` `NavigationContainer`. The state gets represented as a URL-style route. All subsequent events get associated with the view context and thus with the current route.
416
448
 
417
- ```ts
418
- import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-plugin';
449
+ For instance, assume the following setup:
450
+ ```js
451
+ const Drawer = createDrawerNavigator();
419
452
 
420
- const action = Dynatrace.enterManualAction('API Data Upload');
421
- const url = 'https://api.example.com/upload';
422
- const tag = await action.getRequestTag(url);
423
- const timing = new DynatraceWebRequestTiming(tag, url);
424
- const requestData = JSON.stringify({ key: 'value' });
453
+ function App() {
454
+ return (
455
+ <NavigationContainer>
456
+ <Drawer.Navigator initialRouteName="Home">
457
+ <Drawer.Screen name="Home" component={HomeScreen} />
458
+ <Drawer.Screen name="Feature" component={FeatureScreen}/>
459
+ </Drawer.Navigator>
460
+ </NavigationContainer>
461
+ );
462
+ }
425
463
 
426
- try {
427
- timing.startWebRequestTiming();
428
- const response = await fetch(url, {
429
- method: 'POST',
430
- headers: {
431
- [timing.getRequestTagHeader()]: tag,
432
- 'Content-Type': 'application/json'
433
- },
434
- body: requestData
435
- });
436
- const responseData = await response.text();
437
- timing.stopWebRequestTimingWithSize(
438
- response.status,
439
- response.statusText,
440
- requestData.length,
441
- responseData.length
464
+ const Stack = createStackNavigator();
465
+
466
+ function FeatureScreen() {
467
+ return (
468
+ <Stack.Navigator initialRouteName="ScreenOne">
469
+ <Stack.Screen name="ScreenOne" component={NestedScreenOne} />
470
+ <Stack.Screen name="ScreenTwo" component={NestedScreenTwo} />
471
+ </Stack.Navigator>
442
472
  );
443
- } catch (error) {
444
- if (error instanceof Error) {
445
- timing.stopWebRequestTiming(-1, error.message);
446
- } else {
447
- timing.stopWebRequestTiming(-1, (error as any).toString());
448
- }
449
- } finally {
450
- action.leaveAction();
451
473
  }
452
474
  ```
453
- ![rnBytes](https://dt-cdn.net/images/rnbytes-800-139042060c.png)
454
475
 
455
- ### Report values
476
+ When navigating to `Home`, the view context will be set to `/Home`. When navigating to `Feature` and being redirected to the nested `ScreenOne`, the view context will be set to `/Home/ScreenOne`. When navigating to `ScreenTwo` within `Feature`, the view context will be set to `/Feature/ScreenTwo`.
456
477
 
457
- For any open action you can report certain values. The following API is available for action:
478
+ #### Start View
479
+
480
+ Use `startView()` to begin monitoring a specific view or screen. When a view is started, all subsequent events will be associated with that view context.
458
481
 
459
482
  ```ts
460
- import { Platform } from '@dynatrace/react-native-plugin';
483
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
461
484
 
462
- interface IDynatraceAction {
463
- reportDoubleValue(valueName: string, value: number, platform?: Platform): void;
464
- reportError(errorName: string, errorCode: number, platform?: Platform): void;
465
- reportEvent(eventName: string, platform?: Platform): void;
466
- reportIntValue(valueName: string, value: number, platform?: Platform): void;
467
- reportStringValue(valueName: string, value: string, platform?: Platform): void;
468
- }
485
+ // Start monitoring a view
486
+ Dynatrace.startView("HomeScreen");
469
487
  ```
470
488
 
471
- **Important:** All string parameters (errorName, eventName, valueName, value) are limited to 250 characters and will be truncated if they exceed this limit.
489
+ **Important Considerations:**
490
+ * Only one view can be active at a time
491
+ * Starting a new view will automatically stop the previous one
492
+ * View names should be meaningful and consistent across your application
493
+ * All events captured after starting a view will include the view context
472
494
 
473
- To report a string value, use the following:
495
+ **Complete Example:**
474
496
 
475
497
  ```ts
476
- import { Dynatrace } from '@dynatrace/react-native-plugin';
498
+ import { Dynatrace, EventData } from '@dynatrace/react-native-plugin';
477
499
 
478
- const myAction = Dynatrace.enterAutoAction("MyButton tapped");
479
- myAction.reportStringValue("ValueName", "ImportantValue");
480
- myAction.leaveAction();
481
- ```
482
- ![rnReportString](https://dt-cdn.net/images/rnreportstring-800-739e5ba752.png)
500
+ // User navigates to profile screen
501
+ Dynatrace.startView("UserProfile");
483
502
 
484
- If you look at the API calls, you will see the optional parameter `platform?: Platform`. This parameter offers the possibility to report values only for a specific platform. to know more, see [Platform independent reporting](#platform-independent-reporting).
503
+ // Send custom event within the view
504
+ Dynatrace.sendEvent(new EventData()
505
+ .addEventProperty("event_properties.profile_action", "edit_profile")
506
+ .addEventProperty("event_properties.changes_made", true)
507
+ );
485
508
 
509
+ // User navigates away
510
+ Dynatrace.startView("UserProfileDetailed");
511
+ ```
486
512
 
487
- ### Report an error stacktrace
513
+ ### User Interaction
488
514
 
489
- To manually report an error stacktrace, use the following API call:
515
+ User Interaction is an automatic instrumentation feature that captures touch and press events in your React Native application without requiring any manual API calls. When enabled, the plugin instruments your UI components at build time and sends structured interaction events to Dynatrace at runtime.
490
516
 
491
- ```ts
492
- import { Dynatrace } from '@dynatrace/react-native-plugin';
517
+ The following is an example of how this feature can be configured in your `dynatrace.config.js` file. Note that this feature is enabled by default.
493
518
 
494
- try {
495
- throw new Error('Database connection failed');
496
- } catch (error) {
497
- if (error instanceof Error) {
498
- Dynatrace.reportErrorStacktrace(
499
- 'DatabaseError',
500
- error.message,
501
- 'Failed to connect to remote database',
502
- error.stack || 'No stack trace available'
503
- );
504
- }
519
+ ```js
520
+ react: {
521
+ userInteraction: true
505
522
  }
506
523
  ```
507
- ![rnStack](https://dt-cdn.net/images/rnstack-800-26c6ac7408.png)
508
-
509
- **Note:**
510
- The previous API without *errorValue* is deprecated and will be removed in the future. Please use the new API with errorValue if possible.
511
524
 
512
- ### Identify a user
525
+ When enabled, UI Interaction can be controlled by two layers:
513
526
 
514
- You can identify a user and tag the current session with a name by making the following call:
527
+ 1. **Build/config layer (`dynatrace.config.js`)**
528
+ - `react.userInteraction: true|false` controls whether the UI Interaction instrumentation feature is applied.
515
529
 
516
- **Important:** The user identifier is limited to 250 characters and will be truncated if it exceeds this limit.
530
+ 2. **Runtime remote layer**
531
+ - CP always forwards User Interaction Actions (UIA) to the native agent unconditionally. The native agent decides whether to record or discard each UIA based on its own remote configuration (e.g. `touch_interaction_enabled`).
517
532
 
518
- ```ts
519
- import { Dynatrace } from '@dynatrace/react-native-plugin';
533
+ #### Masking sensitive UI labels
520
534
 
521
- Dynatrace.identifyUser("User XY");
522
- ```
523
- ![rnTag](https://dt-cdn.net/images/rntag-800-77fe848023.png)
535
+ UI Interaction also supports masking sensitive labels before events are sent.
524
536
 
525
- ### End the current user session
537
+ Masking is applied when:
526
538
 
527
- To end the current user session, use the following API call:
539
+ * the touched element or one of its parents is marked with `dtMask`
540
+ * the runtime masking rules classify the detected text as sensitive
541
+ * the runtime masking rules classify the element `testID` as sensitive
528
542
 
529
- ```ts
530
- import { Dynatrace } from '@dynatrace/react-native-plugin';
543
+ When masking is active:
531
544
 
532
- Dynatrace.endSession();
533
- ```
545
+ * the interaction path and component information are still reported
546
+ * the detected UI label is replaced with the configured replacement string (default: `***`)
547
+ * `ui_element.name_origin` is reported as `masked`
534
548
 
535
- **Note:** The user tagging will not carry over to the new user session that is started after using this API. If user tagging is desired in the new user session, please ensure that you call the [user tagging](#identify-a-user) API.
549
+ Example using `dtMask`:
536
550
 
537
- ### Manually report a crash
551
+ ```tsx
552
+ <View dtMask>
553
+ <Pressable onPress={onPress}>
554
+ <Text>john.doe@example.com</Text>
555
+ </Pressable>
556
+ </View>
557
+ ```
538
558
 
539
- You can manually report a crash via the following API calls:
559
+ This allows you to preserve interaction analytics while avoiding exposure of sensitive text in the emitted UI Interaction event.
540
560
 
541
- ```ts
542
- import { Dynatrace } from '@dynatrace/react-native-plugin';
543
561
 
544
- try {
545
- throw new Error('Fatal memory allocation failure');
546
- } catch (error) {
547
- if (error instanceof Error) {
548
- Dynatrace.reportCrash(
549
- 'MemoryError',
550
- error.message,
551
- error.stack || 'No stack trace available'
552
- );
562
+ #### Produced data
553
563
 
554
- // or directly via the full error
555
- Dynatrace.reportCrashWithException('MemoryError', error);
556
- }
557
- }
558
- ```
559
- ![rnCrash](https://dt-cdn.net/images/rncrash-800-3e7391140f.png)
564
+ Each captured interaction produces an event describing what the user touched and where. The event includes:
560
565
 
561
- > **Note**: If you use this API call to report a crash manually, it will force the session to be completed. Any new actions that are captured afterwards will be added into a new session.
566
+ - **Detected name** the resolved label of the touched element, derived from its visible text, accessibility label, component name, or test ID.
567
+ - **Component** — the type of the UI component that was touched (e.g. `Pressable`).
568
+ - **Component Path** — a stable path through the component tree that uniquely identifies the element (e.g. `App/View[1]/Pressable[1]`).
569
+ - **Interaction type** — how the user interacted (e.g. `touch`).
570
+ - **Position** — the screen coordinates where the touch occurred.
562
571
 
563
- *reportCrashWithException* will use the crashName as name for the crash. It will only report the crash if there is also a stacktrace available.
572
+ In some cases, a **responder** is also included. The responder is the component that ultimately handled the user's touch — for example, a `Pressable` that received the press event. It carries the same name, component type, and path information as the touched element, and can differ when a touch is visually on a child element but handled by a parent.
564
573
 
565
- ### User Privacy Options
574
+ If masking is active for an interaction, the event still contains the same structural information, but the detected name is replaced and the name origin changes to `masked`.
566
575
 
567
- The privacy API methods allow you to dynamically change the data-collection level based on the individual preferences of your end users. Each end user can select from three data-privacy levels:
576
+ #### Example event
568
577
 
569
- ```ts
570
- export enum DataCollectionLevel {
571
- Off, Performance, UserBehavior
578
+ ```json
579
+ {
580
+ "characteristics.has_user_interaction": true,
581
+ "ui_element.detected_name": "LoginButton",
582
+ "ui_element.components": ["Pressable"],
583
+ "ui_element.id": "App/View/Pressable",
584
+ "ui_element.name_origin": "component",
585
+ "interaction.type": "touch",
586
+ "positions": [{ "x": 120, "y": 460 }],
587
+ "ui_element.responder.detected_name": "Pressable",
588
+ "ui_element.responder.components": ["Pressable"],
589
+ "ui_element.responder.id": "App/View[/Pressable",
590
+ "ui_element.responder.name_origin": "component"
572
591
  }
573
592
  ```
574
593
 
575
- 1. Off: Native Agent doesn't capture any monitoring data.
576
- 2. Performance: Native Agent captures only anonymous performance data. Monitoring data that can be used to identify individual users, such as user tags and custom values, aren't captured.
577
- 3. UserBehavior: Native Agent captures both performance and user data. In this mode, Native Agent recognizes and reports users who re-visit in future sessions.
594
+ #### Custom Names for Components
578
595
 
579
- Crash reporting is enabled by default. The Mobile agent captures all unhandled exceptions/errors and immediately sends the crash report to the server. With this API you can activate or deactivate crash reporting. To change this behaviour via the API, enable/activate [`userOptIn`](#user-opt-in-mode) and set the User Privacy Options.
596
+ You can assign custom names to your components. A custom name affects both the **Component** and the **Component Path** described above. There are two ways to do this, depending on how the component is used.
580
597
 
581
- The API to get and set the current privacy level looks like this:
598
+ ##### 1. As a JSX prop (`dtActionName`)
582
599
 
583
- ```ts
584
- import { Platform, UserPrivacyOptions } from '@dynatrace/react-native-plugin';
600
+ Use this when you render the component as JSX. Pass `dtActionName` as a prop, and it overrides the auto-detected name for that specific usage:
585
601
 
586
- interface IDynatrace {
587
- getUserPrivacyOptions(platform?: Platform): Promise<UserPrivacyOptions>;
588
- applyUserPrivacyOptions(userPrivacyOptions: UserPrivacyOptions, platform?: Platform): void;
589
- }
602
+ ```tsx
603
+ <TouchableOpacity dtActionName="Checkout Button" onPress={onCheckout}>
604
+ <Text dtActionName="Checkout Text">Buy now</Text>
605
+ </TouchableOpacity>
590
606
  ```
591
607
 
592
- To check the current privacy options that are set:
593
-
594
- ```ts
595
- import { Dynatrace } from '@dynatrace/react-native-plugin';
608
+ Touching it produces:
596
609
 
597
- const privacyOptions = await Dynatrace.getUserPrivacyOptions();
610
+ ```json
611
+ "ui_element.components": ["Checkout Text"],
612
+ "ui_element.id": ".../Checkout Button/Checkout Text"
598
613
  ```
599
614
 
600
- If you want to create a new `UserPrivacyOptions` object and pass it to Dynatrace:
601
-
602
- ```ts
603
- import { Dynatrace, DataCollectionLevel, UserPrivacyOptions } from '@dynatrace/react-native-plugin';
615
+ ##### 2. As a static property (`Component.dtActionName`)
604
616
 
605
- const privacyConfig = new UserPrivacyOptions(DataCollectionLevel.UserBehavior, true);
617
+ Use this when the component is **not** rendered as JSX, so the prop approach above is not available — for example, a screen component you define once and hand to a navigator by reference. Set `dtActionName` directly on the component. This affects **every** place the component is used at once:
606
618
 
607
- // Getter and setter available for UserPrivacyOptions
608
- privacyConfig.crashReportingOptedIn = false;
609
- privacyConfig.dataCollectionLevel = DataCollectionLevel.Performance;
619
+ ```tsx
620
+ import { NavigationContainer } from '@react-navigation/native';
621
+ import { createDrawerNavigator } from '@react-navigation/drawer';
610
622
 
611
- const level = privacyConfig.dataCollectionLevel;
612
- const crashReporting = privacyConfig.crashReportingOptedIn;
623
+ const Drawer = createDrawerNavigator();
613
624
 
614
- Dynatrace.applyUserPrivacyOptions(privacyConfig);
615
- ```
625
+ function ProfileScreen() {
626
+ return (/* ... */);
627
+ }
616
628
 
617
- ### Report GPS Location
629
+ // Applies to all usages of ProfileScreen below
630
+ ProfileScreen.dtActionName = 'User Profile';
618
631
 
619
- You can report latitude and longitude and specify an optional platform.
632
+ function App() {
633
+ return (
634
+ <NavigationContainer>
635
+ <Drawer.Navigator>
636
+ {/* Both screens reference the same component,
637
+ so both pick up the 'User Profile' name */}
638
+ <Drawer.Screen name="Me" component={ProfileScreen} />
639
+ <Drawer.Screen name="Account" component={ProfileScreen} />
640
+ </Drawer.Navigator>
641
+ </NavigationContainer>
642
+ );
643
+ }
644
+ ```
620
645
 
621
- ```ts
622
- import { Dynatrace } from '@dynatrace/react-native-plugin';
646
+ Touches inside one of the 2 screens now produce:
623
647
 
624
- Dynatrace.setGPSLocation(48.31518732698596, 14.305245274594471);
648
+ ```json
649
+ "ui_element.id": ".../User Profile/..."
625
650
  ```
626
651
 
627
- ### Platform independent reporting
652
+ > **Note:** The static property applies to *all* uses of the component. If you need a different name per usage, render the component as JSX and use the `dtActionName` prop (option 1) instead.
653
+ >
654
+ > **Note:** The static property has no effect when a component is rendered as JSX. In that case the name is taken from either the `dtActionName` prop passed to it (option 1) or the name of the JSX component.
628
655
 
629
- You probably noticed that each method has an additional *optional* parameter named `platform` of type `Platform`. You can use this to only trigger manual instrumentation for a specific OS. The available values are: `Platform.Ios` and `Platform.Android`. Default is that it will work on any platform. Otherwise it is passed *only* to the relevant OS. For example:
630
- * Passing to **iOS** only:
631
- ```ts
632
- import { Dynatrace, Platform } from '@dynatrace/react-native-plugin';
656
+ ### React Native Symbolication
633
657
 
634
- const myAction = Dynatrace.enterAutoAction("MyButton tapped", Platform.Ios);
635
- //Perform the action and whatever else is needed.
636
- myAction.leaveAction(Platform.Ios);
637
- ```
638
-
639
- * Passing to **Android** only:
640
- ```ts
641
- import { Dynatrace, Platform } from '@dynatrace/react-native-plugin';
658
+ Dynatrace can automatically symbolicate JavaScript stack traces captured by the plugin using sourcemaps. This allows you to view human-readable file names, line numbers, and column information in your crash reports.
642
659
 
643
- const myAction = Dynatrace.enterAutoAction("MyButton tapped", Platform.Android);
644
- //Perform the action and whatever else is needed.
645
- myAction.leaveAction(Platform.Android);
646
- ```
647
-
648
- * Passing to **both**:
649
- ```ts
650
- import { Dynatrace } from '@dynatrace/react-native-plugin';
660
+ #### Generating Sourcemaps
651
661
 
652
- const myAction = Dynatrace.enterAutoAction("MyButton tapped");
653
- //Perform the action and whatever else is needed.
654
- myAction.leaveAction();
655
- ```
662
+ Sourcemaps are generated during release builds and map bytecode offsets (Hermes) and locations in the minified JavaScript bundle (JavaScriptCore) back to your original source code. We support sourcemaps for both Hermes and JavaScriptCore. For detailed instructions on generating sourcemaps, see the [React Native debugging release builds guide](https://reactnative.dev/docs/debugging-release-builds).
656
663
 
657
- ### Business event capturing
664
+ To generate a sourcemap:
658
665
 
659
- With `sendBizEvent`, you can report business events. These events are standalone events, as OneAgent sends them detached from user actions or user sessions.
666
+ **Android:**
667
+ * Run `npx react-native run-android --mode release` in your project root, or
668
+ * Run `gradlew assembleRelease` in the `/android` directory
660
669
 
661
- For more information on business events, see [dynatrace documentation](https://docs.dynatrace.com/docs/observe/business-observability/explore-business-events).
670
+ **iOS:**
671
+ * First, enable sourcemaps according to the [React Native debugging release builds guide](https://reactnative.dev/docs/debugging-release-builds)
672
+ * Then run `npx react-native run-ios --mode Release`, or
673
+ * Build for release in Xcode
662
674
 
663
- ```ts
664
- import { Dynatrace } from '@dynatrace/react-native-plugin';
675
+ #### Uploading Sourcemaps
665
676
 
666
- Dynatrace.sendBizEvent("com.easytravel.funnel.booking-finished", {
667
- "event.name" : "Confirmed Booking",
668
- "screen": "booking-confirmation",
669
- "product": "Danube Anna Hotel",
670
- "amount": 358.35,
671
- "currency": "USD",
672
- "reviewScore": 4.8,
673
- "arrivalDate": "2022-11-05",
674
- "departureDate": "2022-11-15",
675
- "journeyDuration": 10,
676
- "adultTravelers": 2,
677
- "childrenTravelers": 0
678
- });
679
- ```
680
- ![rnBiz](https://dt-cdn.net/images/rnbiz-1262-f57a0a67f3.png)
677
+ Once generated, upload your sourcemaps to Dynatrace. For detailed instructions, see the [symbol file management documentation](https://docs.dynatrace.com/docs/observe/digital-experience/mobile-applications/analyze-and-use/upload-and-manage-symbol-files).
681
678
 
682
- ### Setting beacon headers
679
+ ## RUM Classic
683
680
 
684
- This allows you to put a set of http headers on every agent http request (i.e. Authorization header etc.). It will also triggers the agent to reconnect to the beacon endpoint with the new headers.
681
+ ### Monitor a Component
685
682
 
686
- **Note:** To clear the previously set headers, call the method without the headers parameter or with a null value for the headers parameter.
683
+ A component can be either monitored automatically or manually. The auto instrumentation is handled via the dynatrace.config.js file. If you want to manually instrument a component you can use the API call `withMonitoring`.
684
+
685
+ * Example with Functional Component:
687
686
 
688
687
  ```ts
689
688
  import { Dynatrace } from '@dynatrace/react-native-plugin';
689
+ import { FunctionComponent } from 'react';
690
690
 
691
- const beaconHeaders = new Map<string, string>();
692
- beaconHeaders.set('headerName', 'headerValue');
693
- Dynatrace.setBeaconHeaders(beaconHeaders);
694
- ```
691
+ export const MyFunctionalComponent: FunctionComponent<{}> = () => {
692
+ return null;
693
+ }
695
694
 
696
- ### Exclude Individual JSX Elements
695
+ Dynatrace.withMonitoring(MyFunctionalComponent, "MyFunctionalComponent");
696
+ ```
697
+ ![rnFunctionalComp](https://dt-cdn.net/images/rnfunctionalcomp-800-d98eacef6d.png)
697
698
 
698
- If you want to instrument a functional component or class component but want to exclude a certain button or element, you can do this via the `dtActionIgnore` property. Example:
699
+ The String "MyFunctionalComponent" is optional as the name of the component can be retrieved through [different properties](#how-does-dynatrace-determine-the-user-action-name).
699
700
 
700
- ```tsx
701
- import React from 'react';
702
- import { TouchableHighlight, Text, View } from 'react-native';
701
+ Combining manual and auto instrumentation should not be a problem. As they're running the same execution, the manual instrumentation will only override the content of auto instrumentation happening through the transformer.
703
702
 
704
- const TouchableHighlightScreen = () => {
705
- return (
706
- <View>
707
- <TouchableHighlight onPress={onPress}>
708
- <Text>TouchableHighlight that will be monitored</Text>
709
- </TouchableHighlight>
710
- <TouchableHighlight onPress={onPress} dtActionIgnore="true">
711
- <Text>TouchableHighlight that will be ignored</Text>
712
- </TouchableHighlight>
713
- </View>
714
- );
715
- };
703
+ ### Create custom actions
716
704
 
717
- const onPress = () => {
718
- console.log("TouchableHighlight Pressed!");
719
- };
705
+ There are two options to create an action. Either using `enterAutoAction` (the previous `enterAction`) or `enterManualAction`:
720
706
 
721
- export default TouchableHighlightScreen;
722
- ```
707
+ **Important:** Action names are limited to 250 characters and will be truncated if they exceed this limit.
723
708
 
724
- This example shows two *TouchableHighlight*, which will fire the *onPress()* function when pressed. The property `dtActionIgnore="true"` will prevent the monitoring of one of them. This means that the onPress will still be executed but we will no longer create a user action which is wrapping the button click.
709
+ * `enterAutoAction` - Creates an Action which will be automatically handled by the plugin (This is the type of action which is internally used by the plugin when monitoring components and touchables). This means that the plugin decides about the hierarchy of this action. If there is no open action, the following action will be a root action. All other actions created by this method, while a root action is open, will be automatically inserted as a child action. Furthermore the plugin will automatically link webrequest (if they are not tagged manually) to the open root action. Be aware that the timeout/wait time for sub actions or web requests cannot be modified on the Android side for React Native Auto actions. The timeout is fixed to a 1000ms.
725
710
 
726
- >*Attention:* If you are using Typescript and want to set this property with type-safety, look [here](#typescript-setup-for-dtactionignore-and-dtactionname).
711
+ ```ts
712
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
727
713
 
728
- ## New RUM experience
714
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped");
715
+ //Perform the action and whatever else is needed.
716
+ myAction.leaveAction();
717
+ ```
718
+ ![rnSingle](https://dt-cdn.net/images/rnsingle-800-e7e1b343ed.png)
729
719
 
730
- The New RUM Experience introduces a set of advanced APIs that allow you to send custom events, modify event data, track exceptions, monitor HTTP requests, and manage view contexts in your React Native application. These APIs provide more granular control over the data captured by Dynatrace and are designed for the next generation RUM capabilities.
720
+ * `enterManualAction` - Creates an Action which will NOT be handled by the plugin. This means that you have full control about the hierarchy of your actions. This function will create a root action for you, which has the ability to create child actions via `enterAction`. Be aware, because of the full manual approach the plugin will not link webrequest automatically. Webrequest have to be manually tagged by using the tag provided by the action via `getRequestTag`.
731
721
 
732
- For more detailed information about the New RUM Experience, see the [official Dynatrace documentation](https://docs.dynatrace.com/docs/shortlink/react-native-main).
722
+ ```ts
723
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
733
724
 
734
- ### Send Event
725
+ const myAction = Dynatrace.enterManualAction("MyButton tapped");
726
+ //Perform the action and whatever else is needed.
727
+ myAction.leaveAction();
728
+ ```
735
729
 
736
- The `sendEvent()` method allows you to send custom events with arbitrary properties using the EventData class. This is useful for tracking specific user interactions or application state changes.
730
+ To create a custom action named `"MyButton tapped"`, use the following code. The *leaveAction* closes the action again. To report values for this action before closing, see [Report Values](#report-values).
737
731
 
738
732
  ```ts
739
- import { Dynatrace, EventData } from '@dynatrace/react-native-plugin';
733
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
740
734
 
741
- // Send a custom event with properties
742
- Dynatrace.sendEvent(new EventData()
743
- .addEventProperty("event_properties.button_clicked", "login_button")
744
- .addEventProperty("event_properties.user_type", "premium")
745
- .addEventProperty("event_properties.attempt_count", 3)
746
- .withDuration(250)
747
- );
735
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped");
736
+ //Perform the action and whatever else is needed.
737
+ myAction.leaveAction();
748
738
  ```
749
739
 
750
- **Property Requirements:**
751
- * Only properties prefixed with `event_properties.*` are allowed
752
- * Additionally, the `duration` property is allowed
753
- * Maximum of 50 custom properties per event
754
- * If the limit is exceeded, properties are sorted alphabetically by key and excess properties are dropped deterministically
755
- * String properties are limited to 5000 characters (exceeding characters are truncated)
756
- * Field names must contain only alphabetic characters, numbers, underscores, and dots
757
- * Each dot must be followed by an alphabetic character
758
- * Each underscore must be followed by an alphabetic character or number
759
- * Values must be primitive types (string, number, boolean)
760
- * Cannot contain functions, undefined, Infinity, or NaN as values (they will be replaced with null)
740
+ ### Create custom sub actions
761
741
 
762
- ### Send Session Property Event
742
+ You can create a single custom action as well as sub actions. The `MyButton Sub Action` is automatically put under the `MyButton tapped`. As long as `MyButton tapped` is open, it gathers all the web requests.
763
743
 
764
- Session properties apply to all events within the current session. Use `sendSessionPropertyEvent()` to set properties that should be available across the entire user session.
744
+ **Important:** Action names are limited to 250 characters and will be truncated if they exceed this limit.
765
745
 
766
746
  ```ts
767
- import { Dynatrace, SessionPropertyEventData } from '@dynatrace/react-native-plugin';
747
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
768
748
 
769
- // Set session-wide properties
770
- Dynatrace.sendSessionPropertyEvent(new SessionPropertyEventData()
771
- .addSessionProperty("session_properties.user_tier", "premium")
772
- .addSessionProperty("session_properties.app_version", "2.1.0")
773
- .addSessionProperty("session_properties.feature_flag_enabled", true)
774
- );
749
+ const myAction = Dynatrace.enterManualAction("MyButton tapped");
750
+ const mySubAction = myAction.enterAction("MyButton Sub Action");
751
+ //Perform the action and whatever else is needed.
752
+ mySubAction.leaveAction();
753
+ myAction.leaveAction();
775
754
  ```
755
+ ![subAction](https://dt-cdn.net/images/subaction-800-172dbcca6c.png)
776
756
 
777
- **Important Notes:**
778
- * Session properties persist throughout the entire session
779
- * If you send the same property multiple times, only one value will be kept (first or last)
780
- * Use session properties for data that applies to the entire user session
781
- * Field naming follows the same rules as events, but with `session_properties.` prefix
782
- * Maximum of 50 custom properties per event
783
- * If the limit is exceeded, properties are sorted alphabetically by key and excess properties are dropped deterministically
784
-
785
- ### Event Modifier
757
+ ### Cancel actions
786
758
 
787
- Event modifiers allow you to intercept and modify events before they are sent to Dynatrace. This is useful for adding common properties, filtering sensitive data, or enriching events with additional context. Event modifiers apply to all event types, including custom events, session properties, exceptions, and HTTP events.
759
+ Actions can be canceled. That means they will not be sent and discarded fully. This also means that any values and sub actions attached to the action will be removed.
788
760
 
789
- If multiple event modifiers are added, they are executed in the order they were added.
761
+ ```ts
762
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
790
763
 
791
- Most fields and namespaces can't be modified in any way (added, removed or overridden), while others are open for modification.
764
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped");
765
+ // Action will be canceled
766
+ myAction.cancel();
792
767
 
793
- **Open for modification and can be added:**
794
- - `event_properties.*`
795
- - `session_properties.*`
768
+ // Has no impact as the action is already canceled
769
+ myAction.leaveAction();
770
+ ```
796
771
 
797
- `session_properties.*` are only allowed to be on a session property event.
772
+ ### Manual Web Request Tagging
798
773
 
799
- **Open for modification only:**
800
- - `url.full`
801
- - `exception.stack_trace`
774
+ You can manually tag and time your web requests. With the API shown below, you are able to manually capture the web requests of an http framework/library.
802
775
 
803
- #### Example
776
+ **Note:**
777
+ Using this API will force the request to be added to the action that is manually created.
804
778
 
805
779
  ```ts
806
- import { Dynatrace, IEventModifier } from '@dynatrace/react-native-plugin';
780
+ import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-plugin';
807
781
 
808
- // Create an event modifier
809
- const myModifier: IEventModifier = {
810
- modifyEvent(event) {
811
- // Add common properties to all events
812
- event["event_properties.app_build"] = "1.2.3";
813
- event["event_properties.environment"] = "production";
814
-
815
- // Return null to discard the event entirely
816
- if (event["event_properties.ignore"] === true) {
817
- return null;
782
+ const action = Dynatrace.enterManualAction('API Data Fetch');
783
+ const url = 'https://api.example.com/data';
784
+ const tag = await action.getRequestTag(url);
785
+ const timing = new DynatraceWebRequestTiming(tag, url);
786
+
787
+ try {
788
+ timing.startWebRequestTiming();
789
+ const response = await fetch(url, {
790
+ headers: {
791
+ [timing.getRequestTagHeader()]: tag
818
792
  }
819
-
820
- return event;
793
+ });
794
+ timing.stopWebRequestTiming(response.status, response.statusText);
795
+ } catch (error) {
796
+ if (error instanceof Error) {
797
+ timing.stopWebRequestTiming(-1, error.message);
798
+ } else {
799
+ timing.stopWebRequestTiming(-1, (error as any).toString());
821
800
  }
822
- };
823
-
824
- // Add the modifier
825
- Dynatrace.addEventModifier(myModifier);
826
-
827
- // Remove the modifier when no longer needed
828
- Dynatrace.removeEventModifier(myModifier);
801
+ } finally {
802
+ action.leaveAction();
803
+ }
829
804
  ```
805
+ ![rnManualWeb](https://dt-cdn.net/images/rnmanualweb-800-662d009613.png)
830
806
 
831
- #### Important Considerations
832
-
833
- - **Execution order**: If multiple event modifiers are added, they are executed in the order they were added
834
- - **Returning null**: Returning `null` discards the event and prevents future modifier functions from being executed
835
- - **Performance**: Event modifiers should be efficient as they are called for every event
836
- - **Field naming**: Custom properties must follow the `event_properties.*` or `session_properties.*` prefix naming convention
837
- - **Reserved fields**: Certain reserved fields and namespaces cannot be modified. Attempts to modify them will be ignored
838
- - **Error handling**: If a modifier throws an exception, it will be logged but won't prevent other modifiers from executing
839
- - **Primitive values**: Event fields can only contain primitive values (String, int, double, bool)
840
- - **Invalid argument**: In case you pass an invalid argument to `addEventModifier`, we return a no-op placeholder modifier.
841
-
842
- ### Send Exception Event
843
-
844
- The `sendExceptionEvent()` method provides a structured way to report exceptions with additional context and custom properties using the ExceptionEventData class.
807
+ There is also the option to report values for request and response size:
845
808
 
846
809
  ```ts
847
- import { Dynatrace, ExceptionEventData } from '@dynatrace/react-native-plugin';
810
+ import { Dynatrace, DynatraceWebRequestTiming } from '@dynatrace/react-native-plugin';
811
+
812
+ const action = Dynatrace.enterManualAction('API Data Upload');
813
+ const url = 'https://api.example.com/upload';
814
+ const tag = await action.getRequestTag(url);
815
+ const timing = new DynatraceWebRequestTiming(tag, url);
816
+ const requestData = JSON.stringify({ key: 'value' });
848
817
 
849
818
  try {
850
- // Code that may throw an error
851
- throw new Error('Something went wrong');
819
+ timing.startWebRequestTiming();
820
+ const response = await fetch(url, {
821
+ method: 'POST',
822
+ headers: {
823
+ [timing.getRequestTagHeader()]: tag,
824
+ 'Content-Type': 'application/json'
825
+ },
826
+ body: requestData
827
+ });
828
+ const responseData = await response.text();
829
+ timing.stopWebRequestTimingWithSize(
830
+ response.status,
831
+ response.statusText,
832
+ requestData.length,
833
+ responseData.length
834
+ );
852
835
  } catch (error) {
853
836
  if (error instanceof Error) {
854
- Dynatrace.sendExceptionEvent(new ExceptionEventData(error)
855
- .addEventProperty('event_properties.custom_key', 'custom_value')
856
- .addEventProperty('event_properties.error_context', 'user_action')
857
- );
837
+ timing.stopWebRequestTiming(-1, error.message);
838
+ } else {
839
+ timing.stopWebRequestTiming(-1, (error as any).toString());
858
840
  }
841
+ } finally {
842
+ action.leaveAction();
859
843
  }
860
844
  ```
845
+ ![rnBytes](https://dt-cdn.net/images/rnbytes-800-139042060c.png)
861
846
 
862
- **Parameters:**
863
- * `error`: The Error object containing exception information (required)
864
-
865
- ### Send HTTP Request Event
847
+ ### Report values
866
848
 
867
- The `sendHttpRequestEvent()` method allows you to manually report HTTP request events with detailed information about the request and response.
849
+ For any open action you can report certain values. The following API is available for action:
868
850
 
869
851
  ```ts
870
- import { Dynatrace, HttpRequestEventData } from '@dynatrace/react-native-plugin';
871
-
872
- // Basic HTTP request event
873
- const requestEventData = new HttpRequestEventData('https://api.example.com/users', 'GET');
874
- Dynatrace.sendHttpRequestEvent(requestEventData);
852
+ import { Platform } from '@dynatrace/react-native-plugin';
875
853
 
876
- // HTTP request with additional details
877
- const detailedRequestEventData = new HttpRequestEventData('https://api.example.com/data', 'POST')
878
- .withStatusCode(200)
879
- .addEventProperty('event_properties.headers.content_type', 'application/json');
880
- Dynatrace.sendHttpRequestEvent(detailedRequestEventData);
854
+ interface IDynatraceAction {
855
+ reportDoubleValue(valueName: string, value: number, platform?: Platform): void;
856
+ reportError(errorName: string, errorCode: number, platform?: Platform): void;
857
+ reportEvent(eventName: string, platform?: Platform): void;
858
+ reportIntValue(valueName: string, value: number, platform?: Platform): void;
859
+ reportStringValue(valueName: string, value: string, platform?: Platform): void;
860
+ }
881
861
  ```
882
862
 
883
- **Parameters:**
884
- * `url`: The URL of the HTTP request (required)
885
- * `method`: The HTTP method (e.g., 'GET', 'POST', 'PUT', 'DELETE') (required)
863
+ **Important:** All string parameters (errorName, eventName, valueName, value) are limited to 250 characters and will be truncated if they exceed this limit.
886
864
 
887
- ### View Monitoring
865
+ To report a string value, use the following:
888
866
 
889
- The view monitoring APIs allow you to track different screens or views in your application, providing context for all events happening within those views.
867
+ ```ts
868
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
890
869
 
891
- There are two ways to monitor views:
892
- 1. **Automatic view monitoring** - Enable navigation tracking in your configuration to automatically capture view changes through supported navigation libraries (enabled by default)
893
- 2. **Manual view monitoring** - Use the `startView()` API to manually control when view contexts are created and updated
870
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped");
871
+ myAction.reportStringValue("ValueName", "ImportantValue");
872
+ myAction.leaveAction();
873
+ ```
874
+ ![rnReportString](https://dt-cdn.net/images/rnreportstring-800-739e5ba752.png)
894
875
 
895
- > **Important:** These approaches should not be mixed, as the outcome is unpredictable. Choose either automatic or manual view monitoring for your application.
876
+ If you look at the API calls, you will see the optional parameter `platform?: Platform`. This parameter offers the possibility to report values only for a specific platform. to know more, see [Platform independent reporting](#platform-independent-reporting).
896
877
 
897
- #### Automatic Navigation tracking
898
878
 
899
- > **Note:** This feature only works with `@react-navigation` versions 5.x through 7.x.
879
+ ### Report an error stacktrace
900
880
 
901
- The following is an example of how this feature can be configured in your `dynatrace.config.js` file. Note that this feature is enabled by default.
881
+ To manually report an error stacktrace, use the following API call:
902
882
 
903
- ```js
904
- react: {
905
- navigation: {
906
- enabled: true
883
+ ```ts
884
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
885
+
886
+ try {
887
+ throw new Error('Database connection failed');
888
+ } catch (error) {
889
+ if (error instanceof Error) {
890
+ Dynatrace.reportErrorStacktrace(
891
+ 'DatabaseError',
892
+ error.message,
893
+ 'Failed to connect to remote database',
894
+ error.stack || 'No stack trace available'
895
+ );
907
896
  }
908
897
  }
909
898
  ```
899
+ ![rnStack](https://dt-cdn.net/images/rnstack-800-26c6ac7408.png)
910
900
 
911
- When this feature is enabled, the view context will represent the current state of the `@react-navigation` `NavigationContainer`. The state gets represented as a URL-style route. All subsequent events get associated with the view context and thus with the current route.
901
+ **Note:**
902
+ The previous API without *errorValue* is deprecated and will be removed in the future. Please use the new API with errorValue if possible.
912
903
 
913
- For instance, assume the following setup:
914
- ```js
915
- const Drawer = createDrawerNavigator();
904
+ ### Manually report a crash
916
905
 
917
- function App() {
918
- return (
919
- <NavigationContainer>
920
- <Drawer.Navigator initialRouteName="Home">
921
- <Drawer.Screen name="Home" component={HomeScreen} />
922
- <Drawer.Screen name="Feature" component={FeatureScreen}/>
923
- </Drawer.Navigator>
924
- </NavigationContainer>
925
- );
926
- }
906
+ You can manually report a crash via the following API calls:
927
907
 
928
- const Stack = createStackNavigator();
908
+ ```ts
909
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
929
910
 
930
- function FeatureScreen() {
931
- return (
932
- <Stack.Navigator initialRouteName="ScreenOne">
933
- <Stack.Screen name="ScreenOne" component={NestedScreenOne} />
934
- <Stack.Screen name="ScreenTwo" component={NestedScreenTwo} />
935
- </Stack.Navigator>
936
- );
911
+ try {
912
+ throw new Error('Fatal memory allocation failure');
913
+ } catch (error) {
914
+ if (error instanceof Error) {
915
+ Dynatrace.reportCrash(
916
+ 'MemoryError',
917
+ error.message,
918
+ error.stack || 'No stack trace available'
919
+ );
920
+
921
+ // or directly via the full error
922
+ Dynatrace.reportCrashWithException('MemoryError', error);
923
+ }
937
924
  }
938
925
  ```
926
+ ![rnCrash](https://dt-cdn.net/images/rncrash-800-3e7391140f.png)
939
927
 
940
- When navigating to `Home`, the view context will be set to `/Home`. When navigating to `Feature` and being redirected to the nested `ScreenOne`, the view context will be set to `/Home/ScreenOne`. When navigating to `ScreenTwo` within `Feature`, the view context will be set to `/Feature/ScreenTwo`.
928
+ > **Note**: If you use this API call to report a crash manually, it will force the session to be completed. Any new actions that are captured afterwards will be added into a new session.
941
929
 
942
- #### Start View
930
+ *reportCrashWithException* will use the crashName as name for the crash. It will only report the crash if there is also a stacktrace available.
943
931
 
944
- Use `startView()` to begin monitoring a specific view or screen. When a view is started, all subsequent events will be associated with that view context.
932
+ ### Report GPS Location
933
+
934
+ You can report latitude and longitude and specify an optional platform.
945
935
 
946
936
  ```ts
947
937
  import { Dynatrace } from '@dynatrace/react-native-plugin';
948
938
 
949
- // Start monitoring a view
950
- Dynatrace.startView("HomeScreen");
951
- ```
952
-
953
- **Important Considerations:**
954
- * Only one view can be active at a time
955
- * Starting a new view will automatically stop the previous one
956
- * View names should be meaningful and consistent across your application
957
- * All events captured after starting a view will include the view context
939
+ Dynatrace.setGPSLocation(48.31518732698596, 14.305245274594471);
940
+ ```
958
941
 
959
- **Complete Example:**
942
+ ### Platform independent reporting
960
943
 
944
+ You probably noticed that each method has an additional *optional* parameter named `platform` of type `Platform`. You can use this to only trigger manual instrumentation for a specific OS. The available values are: `Platform.Ios` and `Platform.Android`. Default is that it will work on any platform. Otherwise it is passed *only* to the relevant OS. For example:
945
+ * Passing to **iOS** only:
961
946
  ```ts
962
- import { Dynatrace, EventData } from '@dynatrace/react-native-plugin';
947
+ import { Dynatrace, Platform } from '@dynatrace/react-native-plugin';
963
948
 
964
- // User navigates to profile screen
965
- Dynatrace.startView("UserProfile");
949
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped", Platform.Ios);
950
+ //Perform the action and whatever else is needed.
951
+ myAction.leaveAction(Platform.Ios);
952
+ ```
953
+
954
+ * Passing to **Android** only:
955
+ ```ts
956
+ import { Dynatrace, Platform } from '@dynatrace/react-native-plugin';
966
957
 
967
- // Send custom event within the view
968
- Dynatrace.sendEvent(new EventData()
969
- .addEventProperty("event_properties.profile_action", "edit_profile")
970
- .addEventProperty("event_properties.changes_made", true)
971
- );
958
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped", Platform.Android);
959
+ //Perform the action and whatever else is needed.
960
+ myAction.leaveAction(Platform.Android);
961
+ ```
962
+
963
+ * Passing to **both**:
964
+ ```ts
965
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
972
966
 
973
- // User navigates away
974
- Dynatrace.startView("UserProfileDetailed");
967
+ const myAction = Dynatrace.enterAutoAction("MyButton tapped");
968
+ //Perform the action and whatever else is needed.
969
+ myAction.leaveAction();
975
970
  ```
976
971
 
977
- ### User Interaction
972
+ ### Business event capturing
978
973
 
979
- User Interaction is an automatic instrumentation feature that captures touch and press events in your React Native application without requiring any manual API calls. When enabled, the plugin instruments your UI components at build time and sends structured interaction events to Dynatrace at runtime.
974
+ With `sendBizEvent`, you can report business events. These events are standalone events, as OneAgent sends them detached from user actions or user sessions.
980
975
 
981
- The following is an example of how this feature can be configured in your `dynatrace.config.js` file. Note that this feature is enabled by default.
976
+ For more information on business events, see [dynatrace documentation](https://docs.dynatrace.com/docs/observe/business-observability/explore-business-events).
982
977
 
983
- ```js
984
- react: {
985
- userInteraction: true
986
- }
978
+ ```ts
979
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
980
+
981
+ Dynatrace.sendBizEvent("com.easytravel.funnel.booking-finished", {
982
+ "event.name" : "Confirmed Booking",
983
+ "screen": "booking-confirmation",
984
+ "product": "Danube Anna Hotel",
985
+ "amount": 358.35,
986
+ "currency": "USD",
987
+ "reviewScore": 4.8,
988
+ "arrivalDate": "2022-11-05",
989
+ "departureDate": "2022-11-15",
990
+ "journeyDuration": 10,
991
+ "adultTravelers": 2,
992
+ "childrenTravelers": 0
993
+ });
987
994
  ```
995
+ ![rnBiz](https://dt-cdn.net/images/rnbiz-1262-f57a0a67f3.png)
988
996
 
989
- When enabled, UI Interaction can be controlled by two layers:
997
+ ### Setting beacon headers
990
998
 
991
- 1. **Build/config layer (`dynatrace.config.js`)**
992
- - `react.userInteraction: true|false` controls whether the UI Interaction instrumentation feature is applied.
999
+ This allows you to put a set of http headers on every agent http request (i.e. Authorization header etc.). It will also triggers the agent to reconnect to the beacon endpoint with the new headers.
993
1000
 
994
- 2. **Runtime remote layer (`RuntimeConfigurationObserver`)**
995
- - Runtime emission checks the remote flag `touch_interaction_enabled`.
996
- - If remote flag is present, it is used as the source of truth.
997
- - If remote flag is temporarily unavailable, the plugin falls back to the last known good remote value.
998
- - If no remote value was received yet, runtime defaults to enabled behavior.
1001
+ **Note:** To clear the previously set headers, call the method without the headers parameter or with a null value for the headers parameter.
999
1002
 
1000
- #### Masking sensitive UI labels
1003
+ ```ts
1004
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
1001
1005
 
1002
- UI Interaction also supports masking sensitive labels before events are sent.
1006
+ const beaconHeaders = new Map<string, string>();
1007
+ beaconHeaders.set('headerName', 'headerValue');
1008
+ Dynatrace.setBeaconHeaders(beaconHeaders);
1009
+ ```
1003
1010
 
1004
- Masking is applied when:
1011
+ ### Exclude Individual JSX Elements
1005
1012
 
1006
- * the touched element or one of its parents is marked with `dtMask`
1007
- * the runtime masking rules classify the detected text as sensitive
1008
- * the runtime masking rules classify the element `testID` as sensitive
1013
+ If you want to instrument a functional component or class component but want to exclude a certain button or element, you can do this via the `dtActionIgnore` property. Example:
1009
1014
 
1010
- When masking is active:
1015
+ ```tsx
1016
+ import React from 'react';
1017
+ import { TouchableHighlight, Text, View } from 'react-native';
1011
1018
 
1012
- * the interaction path and component information are still reported
1013
- * the detected UI label is replaced with the configured replacement string (default: `***`)
1014
- * `ui_element.name_origin` is reported as `masked`
1019
+ const TouchableHighlightScreen = () => {
1020
+ return (
1021
+ <View>
1022
+ <TouchableHighlight onPress={onPress}>
1023
+ <Text>TouchableHighlight that will be monitored</Text>
1024
+ </TouchableHighlight>
1025
+ <TouchableHighlight onPress={onPress} dtActionIgnore="true">
1026
+ <Text>TouchableHighlight that will be ignored</Text>
1027
+ </TouchableHighlight>
1028
+ </View>
1029
+ );
1030
+ };
1015
1031
 
1016
- Example using `dtMask`:
1032
+ const onPress = () => {
1033
+ console.log("TouchableHighlight Pressed!");
1034
+ };
1017
1035
 
1018
- ```tsx
1019
- <View dtMask>
1020
- <Pressable onPress={onPress}>
1021
- <Text>john.doe@example.com</Text>
1022
- </Pressable>
1023
- </View>
1036
+ export default TouchableHighlightScreen;
1024
1037
  ```
1025
1038
 
1026
- This allows you to preserve interaction analytics while avoiding exposure of sensitive text in the emitted UI Interaction event.
1027
-
1039
+ This example shows two *TouchableHighlight*, which will fire the *onPress()* function when pressed. The property `dtActionIgnore="true"` will prevent the monitoring of one of them. This means that the onPress will still be executed but we will no longer create a user action which is wrapping the button click.
1028
1040
 
1029
- #### Produced data
1041
+ >*Attention:* If you are using Typescript and want to set this property with type-safety, look [here](#typescript-setup-for-dtactionignore-and-dtactionname).
1030
1042
 
1031
- Each captured interaction produces an event describing what the user touched and where. The event includes:
1043
+ ## Extended topics
1032
1044
 
1033
- - **Detected name** — the resolved label of the touched element, derived from its visible text, accessibility label, component name, or test ID.
1034
- - **Component** — the type of the UI component that was touched (e.g. `Pressable`).
1035
- - **Component Path** — a stable path through the component tree that uniquely identifies the element (e.g. `App/View[1]/Pressable[1]`).
1036
- - **Interaction type** — how the user interacted (e.g. `touch`).
1037
- - **Position** — the screen coordinates where the touch occurred.
1045
+ ### Identify a user
1038
1046
 
1039
- In some cases, a **responder** is also included. The responder is the component that ultimately handled the user's touch — for example, a `Pressable` that received the press event. It carries the same name, component type, and path information as the touched element, and can differ when a touch is visually on a child element but handled by a parent.
1047
+ You can identify a user and tag the current session with a name by making the following call:
1040
1048
 
1041
- If masking is active for an interaction, the event still contains the same structural information, but the detected name is replaced and the name origin changes to `masked`.
1049
+ **Important:** The user identifier is limited to 250 characters and will be truncated if it exceeds this limit.
1042
1050
 
1043
- #### Example event
1051
+ ```ts
1052
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
1044
1053
 
1045
- ```json
1046
- {
1047
- "characteristics.has_user_interaction": true,
1048
- "ui_element.detected_name": "LoginButton",
1049
- "ui_element.components": ["Pressable"],
1050
- "ui_element.id": "App/View/Pressable",
1051
- "ui_element.name_origin": "component",
1052
- "interaction.type": "touch",
1053
- "positions": [{ "x": 120, "y": 460 }],
1054
- "ui_element.responder.detected_name": "Pressable",
1055
- "ui_element.responder.components": ["Pressable"],
1056
- "ui_element.responder.id": "App/View[/Pressable",
1057
- "ui_element.responder.name_origin": "component"
1058
- }
1054
+ Dynatrace.identifyUser("User XY");
1059
1055
  ```
1056
+ ![rnTag](https://dt-cdn.net/images/rntag-800-77fe848023.png)
1060
1057
 
1061
- #### Custom Names for Components
1062
-
1063
- You can assign custom names to your components. A custom name affects both the **Component** and the **Component Path** described above. There are two ways to do this, depending on how the component is used.
1058
+ ### End the current user session
1064
1059
 
1065
- ##### 1. As a JSX prop (`dtActionName`)
1060
+ To end the current user session, use the following API call:
1066
1061
 
1067
- Use this when you render the component as JSX. Pass `dtActionName` as a prop, and it overrides the auto-detected name for that specific usage:
1062
+ ```ts
1063
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
1068
1064
 
1069
- ```tsx
1070
- <TouchableOpacity dtActionName="Checkout Button" onPress={onCheckout}>
1071
- <Text dtActionName="Checkout Text">Buy now</Text>
1072
- </TouchableOpacity>
1065
+ Dynatrace.endSession();
1073
1066
  ```
1074
1067
 
1075
- Touching it produces:
1068
+ **Note:** The user tagging will not carry over to the new user session that is started after using this API. If user tagging is desired in the new user session, please ensure that you call the [user tagging](#identify-a-user) API.
1076
1069
 
1077
- ```json
1078
- "ui_element.components": ["Checkout Text"],
1079
- "ui_element.id": ".../Checkout Button/Checkout Text"
1080
- ```
1070
+ ### User Privacy Options
1081
1071
 
1082
- ##### 2. As a static property (`Component.dtActionName`)
1072
+ The privacy API methods allow you to dynamically change the data-collection level based on the individual preferences of your end users. Each end user can select from three data-privacy levels:
1083
1073
 
1084
- Use this when the component is **not** rendered as JSX, so the prop approach above is not available — for example, a screen component you define once and hand to a navigator by reference. Set `dtActionName` directly on the component. This affects **every** place the component is used at once:
1074
+ ```ts
1075
+ export enum DataCollectionLevel {
1076
+ Off, Performance, UserBehavior
1077
+ }
1078
+ ```
1085
1079
 
1086
- ```tsx
1087
- import { NavigationContainer } from '@react-navigation/native';
1088
- import { createDrawerNavigator } from '@react-navigation/drawer';
1080
+ 1. Off: Native Agent doesn't capture any monitoring data.
1081
+ 2. Performance: Native Agent captures only anonymous performance data. Monitoring data that can be used to identify individual users, such as user tags and custom values, aren't captured.
1082
+ 3. UserBehavior: Native Agent captures both performance and user data. In this mode, Native Agent recognizes and reports users who re-visit in future sessions.
1089
1083
 
1090
- const Drawer = createDrawerNavigator();
1084
+ Crash reporting is enabled by default. The Mobile agent captures all unhandled exceptions/errors and immediately sends the crash report to the server. With this API you can activate or deactivate crash reporting. To change this behaviour via the API, enable/activate [`userOptIn`](#user-opt-in-mode) and set the User Privacy Options.
1091
1085
 
1092
- function ProfileScreen() {
1093
- return (/* ... */);
1094
- }
1086
+ The API to get and set the current privacy level looks like this:
1095
1087
 
1096
- // Applies to all usages of ProfileScreen below
1097
- ProfileScreen.dtActionName = 'User Profile';
1088
+ ```ts
1089
+ import { Platform, UserPrivacyOptions } from '@dynatrace/react-native-plugin';
1098
1090
 
1099
- function App() {
1100
- return (
1101
- <NavigationContainer>
1102
- <Drawer.Navigator>
1103
- {/* Both screens reference the same component,
1104
- so both pick up the 'User Profile' name */}
1105
- <Drawer.Screen name="Me" component={ProfileScreen} />
1106
- <Drawer.Screen name="Account" component={ProfileScreen} />
1107
- </Drawer.Navigator>
1108
- </NavigationContainer>
1109
- );
1091
+ interface IDynatrace {
1092
+ getUserPrivacyOptions(platform?: Platform): Promise<UserPrivacyOptions>;
1093
+ applyUserPrivacyOptions(userPrivacyOptions: UserPrivacyOptions, platform?: Platform): void;
1110
1094
  }
1111
1095
  ```
1112
1096
 
1113
- Touches inside one of the 2 screens now produce:
1114
-
1115
- ```json
1116
- "ui_element.id": ".../User Profile/..."
1117
- ```
1118
-
1119
- > **Note:** The static property applies to *all* uses of the component. If you need a different name per usage, render the component as JSX and use the `dtActionName` prop (option 1) instead.
1120
- >
1121
- > **Note:** The static property has no effect when a component is rendered as JSX. In that case the name is taken from either the `dtActionName` prop passed to it (option 1) or the name of the JSX component.
1122
-
1123
- ### React Native Symbolication
1097
+ To check the current privacy options that are set:
1124
1098
 
1125
- Dynatrace can automatically symbolicate JavaScript stack traces captured by the plugin using sourcemaps. This allows you to view human-readable file names, line numbers, and column information in your crash reports.
1099
+ ```ts
1100
+ import { Dynatrace } from '@dynatrace/react-native-plugin';
1126
1101
 
1127
- #### Generating Sourcemaps
1102
+ const privacyOptions = await Dynatrace.getUserPrivacyOptions();
1103
+ ```
1128
1104
 
1129
- Sourcemaps are generated during release builds and map bytecode offsets (Hermes) and locations in the minified JavaScript bundle (JavaScriptCore) back to your original source code. We support sourcemaps for both Hermes and JavaScriptCore. For detailed instructions on generating sourcemaps, see the [React Native debugging release builds guide](https://reactnative.dev/docs/debugging-release-builds).
1105
+ If you want to create a new `UserPrivacyOptions` object and pass it to Dynatrace:
1130
1106
 
1131
- To generate a sourcemap:
1107
+ ```ts
1108
+ import { Dynatrace, DataCollectionLevel, UserPrivacyOptions } from '@dynatrace/react-native-plugin';
1132
1109
 
1133
- **Android:**
1134
- * Run `npx react-native run-android --mode release` in your project root, or
1135
- * Run `gradlew assembleRelease` in the `/android` directory
1110
+ const privacyConfig = new UserPrivacyOptions(DataCollectionLevel.UserBehavior, true);
1136
1111
 
1137
- **iOS:**
1138
- * First, enable sourcemaps according to the [React Native debugging release builds guide](https://reactnative.dev/docs/debugging-release-builds)
1139
- * Then run `npx react-native run-ios --mode Release`, or
1140
- * Build for release in Xcode
1112
+ // Getter and setter available for UserPrivacyOptions
1113
+ privacyConfig.crashReportingOptedIn = false;
1114
+ privacyConfig.dataCollectionLevel = DataCollectionLevel.Performance;
1141
1115
 
1142
- #### Uploading Sourcemaps
1116
+ const level = privacyConfig.dataCollectionLevel;
1117
+ const crashReporting = privacyConfig.crashReportingOptedIn;
1143
1118
 
1144
- Once generated and patched, upload your sourcemaps to Dynatrace. For detailed instructions, see the [symbol file management documentation](https://docs.dynatrace.com/docs/observe/digital-experience/mobile-applications/analyze-and-use/upload-and-manage-symbol-files).
1119
+ Dynatrace.applyUserPrivacyOptions(privacyConfig);
1120
+ ```
1145
1121
 
1146
1122
  ## NPX Commands
1147
1123
 
@@ -1149,7 +1125,6 @@ The following npx commands are available for the plugin:
1149
1125
 
1150
1126
  * npx instrumentDynatrace - Is triggering the configuration process and will insert the configuration into the Android and iOS application. This is mandatory and should usually happen automatically when doing `react-native run-android` or `react-native run-ios` command.
1151
1127
  * npx configDynatrace - Is checking the current configuration and is creating a default configuration if there is none.
1152
- * npx lineOffsetDynatrace - ...
1153
1128
 
1154
1129
  ### npx instrumentDynatrace
1155
1130
 
@@ -1520,18 +1495,6 @@ The changes our auto-instrumentation is making now get dumped into the `node_mod
1520
1495
 
1521
1496
  > **Note:** This feature increases the time it takes to build the Javascript bundle. Only use it for debugging purposes. Deactivate it otherwise, especially when fast build times are important.
1522
1497
 
1523
- #### Using our legacy jscodeshift auto-instrumentation
1524
-
1525
- We recently moved our auto-instrumentation to a babel plugin. In case you need to, you can still switch to the old jscodeshift auto-instrumentation with this flag:
1526
-
1527
- ```js
1528
- react: {
1529
- useLegacyJscodeshift: true
1530
- }
1531
- ```
1532
-
1533
- > **Note:** When we moved our auto-instrumentation to a babel plugin, we removed documentation that was only relevant for the jscodeshift auto-instrumentation. Most notably, we removed documentation concerning registering a custom metro transformer and patching sourcemaps. If you use the `useLegacyJscodeshift` flag, please refer to the [legacy documentation (v2.333.1)](https://www.npmjs.com/package/@dynatrace/react-native-plugin/v/2.333.1) for details on custom metro transformers and sourcemap patching.
1534
-
1535
1498
  ### Android block
1536
1499
 
1537
1500
  The Android block is a wrapper for the Android configuration you find in the WebUI (in the Mobile Application Settings). Copy the content into the following block:
@@ -1701,7 +1664,7 @@ ios: {
1701
1664
  * class name: If the display name is not available, the class name is used by taking the property name from the constructor
1702
1665
  * Touchables, Buttons, Pressable
1703
1666
  * dtActionName: Use a custom property called dtActionName
1704
- * If [actionNamePrivacy](#plugin-startup) is activated anything below will not be detected
1667
+ * If [actionNamePrivacy](#manual-oneagent-startup) is activated anything below will not be detected
1705
1668
  * title property
1706
1669
  * accessibilityLabel property
1707
1670
  * If none of the above exist, we recursively traverse `children` to find a name. We consider text, the source property of any `ReactNative.Image` and the name property of any functional component called `Icon`.
@@ -1751,7 +1714,7 @@ import { Text } from 'react-native';
1751
1714
  </TouchableHighlight>
1752
1715
  ```
1753
1716
 
1754
- *Note:* [actionNamePrivacy](#plugin-startup) has no impact on using dtActionName. dtActionName will always be used.
1717
+ *Note:* [actionNamePrivacy](#manual-oneagent-startup) has no impact on using dtActionName. dtActionName will always be used.
1755
1718
 
1756
1719
  >*Attention:* If you are using Typescript and want to set this property with type-safety, look [here](#typescript-setup-for-dtactionignore-and-dtactionname).
1757
1720
 
@@ -1765,7 +1728,7 @@ In general, Dynatrace always closes the session when a crash occurs. Usually a c
1765
1728
  * Fatal error: If the error is considered as fatal by the React Native crash handler, it will be treated as crash.
1766
1729
  * Native crashes: If a crash appears solely on the native side and is not noticed by the internal React Native crash handler, it will be reported directly by the Android or iOS agent.
1767
1730
  * Exception: Some JavaScript crashes will result in Native crashes. Therefore, they are filtered on the native side as normally the crash would be reported twice. On the JavaScript side, these crashes are already reported by us before they hit the native side resulting in no lost information.
1768
- Additionally, you can use our crash and error reporting APIs which are available with [manual instrumentation](#manual-instrumentation).
1731
+ Additionally, you can use our crash and error reporting APIs which are available with [RUM Classic](#rum-classic).
1769
1732
 
1770
1733
 
1771
1734
  ## React Automatic Runtime
@@ -2088,6 +2051,20 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2088
2051
  <br/><br/>
2089
2052
  ## Changelog
2090
2053
 
2054
+ In Progress
2055
+
2056
+ 2.345.1
2057
+ * Updated Android (8.345.1.1003) & iOS Agent (8.345.1.1003)
2058
+ * Renamed `monitorNavigation` to `useNavigationMonitor`; React Navigation screen changes are now detected automatically, stamped with `start_time` and `duration`, and trigger Session Replay capture after the incoming screen settles
2059
+ * Added React Navigation support: settle detection on both Android and iOS bridges, UI-change capture toggling restored
2060
+ * Removed the bridge-side settle helper — the native agent now owns coalescing on both platforms
2061
+ * Added a runtime deprecation warning system
2062
+ * Removed the option to use the legacy jscodeshift auto-instrumentation by removing the `useLegacyJscodeshift` flag from `dynatrace.config.js`
2063
+ * Removed leading slash from reported view names
2064
+ * Discard user interaction actions when 3rd-gen instrumentation is disabled, even without an `onPress` callback
2065
+ * Android: compatibility fixes for AGP 9 and Kotlin 2.2 (React Native 0.87)
2066
+ * iOS: app-start measurement fixes for the new architecture
2067
+
2091
2068
  2.343.1
2092
2069
  * Updated Android (8.343.1.1038) & iOS Agent (8.343.1.1007)
2093
2070
  * Added User Actions feature
@@ -2101,6 +2078,7 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2101
2078
  * Added `DYNATRACE_CONFIG` environment variable as an alternative way to specify the config file path for the Metro/Babel transformer.
2102
2079
  * Added `npx instrumentDynatrace --reset` flag to clear a previously-persisted custom config path and revert the Metro/Babel transformer to the default `dynatrace.config.js` lookup. Useful after switching between build flavors that each use a different config file.
2103
2080
  * Fixed incorrect `start_time` on Android app start events when `3rd_gen_enabled` is false. The `EventPipeline` was suppressing the `forwardAppStartEvent` timing-correction call along with all other 3rd-gen events, causing the native Dynatrace SDK to report its own stale process-creation timestamp instead of the actual JS bundle load time.
2081
+ * CP now always forwards User Interaction Actions (UIA) to the native agent unconditionally, regardless of the `touch_interaction_enabled` server configuration flag. The native agent is responsible for deciding whether to record or discard each UIA. This applies to both the Babel-instrumented (`AnalyticsRoot`/`TouchCapture`) and the legacy JSX-wrapped Touchables UIA paths.
2104
2082
 
2105
2083
  2.341.1
2106
2084
  * Updated Android (8.341.1.1004) & iOS Agent (8.341.1.1010)
@@ -2128,7 +2106,7 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2128
2106
  * Updated Android (8.337.2.1010) & iOS Agent (8.337.1.1003)
2129
2107
  * Fixed gradle path derivation for custom `--gradle` arguments: the plugin now correctly probes the filesystem to auto-detect `app/build.gradle(.kts)` with proper Kotlin DSL support, and validates that only `build.gradle` or `build.gradle.kts` files are accepted (not `settings.gradle`).
2130
2108
  * Enables [pnpm monorepo instrumentation](#pnpm-monorexpo-setup) by allowing explicit `config=`, `gradle=`, and `plist=` arguments in root `package.json` scripts, supporting shared native project layouts where `android/` and `ios/` are not co-located with each app.
2131
- * Added [actionNamePreference](#plugin-startup) and [actionNameAlgorithm](#plugin-startup) configuration flags.
2109
+ * Added [actionNamePreference](#manual-oneagent-startup) and [actionNameAlgorithm](#manual-oneagent-startup) configuration flags.
2132
2110
 
2133
2111
  2.335.1
2134
2112
  * Updated Android (8.335.1.1001) & iOS Agent (8.335.1.1009)
@@ -2152,12 +2130,12 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2152
2130
 
2153
2131
  2.329.1
2154
2132
  * Updated Android (8.329.1.1014) & iOS Agent (8.329.1.1017)
2155
- * [New RUM experience](#new-rum-experience) Removed Dynatrace.stopView() API
2156
- * [New RUM experience](#new-rum-experience) Enforcing length limitation of 5000 characters for user input values
2157
- * [New RUM experience](#new-rum-experience) React Native Deobfuscation service via sourcemaps
2133
+ * [New RUM experience](#real-user-monitoring-rum) Removed Dynatrace.stopView() API
2134
+ * [New RUM experience](#real-user-monitoring-rum) Enforcing length limitation of 5000 characters for user input values
2135
+ * [New RUM experience](#real-user-monitoring-rum) React Native Deobfuscation service via sourcemaps
2158
2136
  * Adding [`npx lineOffsetDynatrace`](#npx-lineoffsetdynatrace) command
2159
- * [New RUM experience](#new-rum-experience) Added automated view tracking via [navigation](#automatic-navigation-tracking)
2160
- * [New RUM experience](#new-rum-experience) Event and session properties are now sorted alphabetically when the limit of 50 is exceeded, ensuring deterministic property dropping
2137
+ * [New RUM experience](#real-user-monitoring-rum) Added automated view tracking via [navigation](#automatic-navigation-tracking)
2138
+ * [New RUM experience](#real-user-monitoring-rum) Event and session properties are now sorted alphabetically when the limit of 50 is exceeded, ensuring deterministic property dropping
2161
2139
 
2162
2140
  2.327.2
2163
2141
  * Fixed iOS startup time calculation issues for React Native 0.72 and lower
@@ -2166,7 +2144,7 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2166
2144
 
2167
2145
  2.327.1
2168
2146
  * Added [Compose Compatibility Note](#compose-compatibility-note)
2169
- * Added [New RUM experience preview](#new-rum-experience) documentation
2147
+ * Added [New RUM experience preview](#real-user-monitoring-rum) documentation
2170
2148
  * Fixed iOS crash reporting by delaying the crash by 200ms on iOS to give the Agent enough time to report it
2171
2149
  * Updated Android (8.327.2.1004) & iOS Agent (8.327.1.1020)
2172
2150
 
@@ -2175,8 +2153,8 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2175
2153
 
2176
2154
  2.325.1
2177
2155
  * Updated Android (8.325.1.1007) & iOS Agent (8.325.1.1012)
2178
- * [New RUM experience preview](#new-rum-experience) ReactNative version now added to RUM on Grail event base data
2179
- * [New RUM experience preview](#new-rum-experience) Added API documentation for RUM on Grail API
2156
+ * [New RUM experience preview](#real-user-monitoring-rum) ReactNative version now added to RUM on Grail event base data
2157
+ * [New RUM experience preview](#real-user-monitoring-rum) Added API documentation for RUM on Grail API
2180
2158
 
2181
2159
  2.323.2
2182
2160
  * Fixed error "Execution failed for task ':app:mergeReleaseAssets'. A problem occured starting process 'command 'npx''" when building for release on Windows.
@@ -2362,7 +2340,7 @@ If you are struggling with a problem, submit a support ticket to Dynatrace (supp
2362
2340
  2.247.1
2363
2341
  * Updated Android Agent (8.247.1.1003) & iOS Agent (8.247.1.1007)
2364
2342
  * Minimum supported Android SDK version raised to 21
2365
- * Added [actionNamePrivacy](#plugin-startup) configuration flag
2343
+ * Added [actionNamePrivacy](#manual-oneagent-startup) configuration flag
2366
2344
  * Removed `instrumentDynatrace` from package.json
2367
2345
  * Adding [`npx instrumentDynatrace`](#npx-instrumentdynatrace) and [`npx configDynatrace`](#npx-configdynatrace) command
2368
2346