@dynatrace/react-native-plugin

This plugin gives you the ability to use the Dynatrace Mobile agent in your react native application.

Usage no npm install needed!

<script type="module">
  import dynatraceReactNativePlugin from 'https://cdn.skypack.dev/@dynatrace/react-native-plugin';
</script>

README

N|Solid

Dynatrace React Native Plugin

The Dynatrace React Native plugin helps auto-instrument your React Native app with Dynatrace OneAgent for Android and iOS and also provides an API to add manual instrumentation. It is compatible with raw, ejected React Native projects, which means it works with Expo Kit, but not with Expo.

If you want to start using this plugin and are not a Dynatrace customer yet, head to dynatrace.com and sign up for a free trial. For an intro you can also check out our announcement blog post.

Supported features

  • Auto-instrumentation using OneAgent for Android and iOS
    • User actions for application start and native controls
    • Web requests
    • Crashes
  • React-native Auto-instrumentation
    • User actions for onPress and onLongPress (Touchables, Buttons, Pickers, RefreshControl, Pressable)
    • User actions for class and functional components (lifecycle events such as render(), didMount() and didUpdate())
    • Reporting React Native errors
  • Manual instrumentation
    • Typescript bindings to add manual instrumentation

Requirements

  • React >= 16.8
  • React Native >= 0.59
  • Gradle >= 5.0 (How to upgrade?)
  • For Android users: Minimum SDK version 15
  • For iOS users: Minimum iOS 9

Agent Versions

This agent versions are configured in this plugin:

  • iOS Agent: 8.231.1.1009
  • Android Agent: 8.231.2.1007

Quick Setup

  1. Install plugin
  2. Register Dynatrace transformer
  3. Setup configuration
  4. Build and run your app

Advanced topics

Troubleshooting

Quick Setup

1. Install the plugin

  1. Install the plugin by calling:
    • => RN 0.60.0 : npm install @dynatrace/react-native-plugin
    • < RN 0.60.0 : react-native install @dynatrace/react-native-plugin.
  2. iOS only : If you use pods, you need to go into your ios directory and execute pod install to install the new Dynatrace dependecy to your xCode project.

Troubleshooting

  • Expo-Kit only: The installation script might not be triggered automatically. You can call it manually by running node ./node_modules/@dynatrace/react-native-plugin/scripts/install.js (You will notice this when the npm run instrumentDynatrace call is not available.).
  • Standalone Project: If you are using React Native standalone and embed it in your native project have a look here.
  • If for some reason (e.g. seperate native projects) react-native link doesn't work as expected, manually add the iOS agent to your project.

2. Register the Dynatrace transformer

Depending on your React Native version, you will need to use a different way to register the transformer. If you don't know the version, enter react-native --version in your terminal.

The following configuration must be added. Any configuration that is already in the file can remain. If you already have a registered transformer, create a custom transformer that is calling several transformers.

In your project's root directory, create or extend metro.config.js so that it contains the transformer.babelTransformerPath property:

module.exports = {
  transformer: {
    babelTransformerPath: require.resolve('@dynatrace/react-native-plugin/lib/dynatrace-transformer')
  },

  reporter: require("@dynatrace/react-native-plugin/lib/dynatrace-reporter")
};

3. Setup dynatrace.config.js

Note: If you are upgrading from a previous version of this plugin, you'll notice that the file format has changed. Your old configuration is still available in dynatrace.config and you have to copy your values to the new dynatrace.config.js.

Define a mobile app in Dynatrace and open the Mobile app instrumentation settings. In the settings you will see a snippet for Android and iOS. Open the dynatrace.config.js in the root directory of your project that was already created by the npm install script and copy the snippets in there. You will see that the dynatrace.config.js already contains a default snippet for Android and iOS, but without correct values.

Note: Define the components that you want to see lifecycle instrumented (example). This is important as you will only see Application startup and Touches out of the box.

For more details about the configuration, see Advanced topics.

4. Build and run your app

  1. Only RN < 0.60: Execute npm run instrumentDynatrace or react-native instrument-dynatrace in the root of your React Native project. This will configure both Android and iOS projects with the settings from dynatrace.config.js. You can use the same custom arguments as mentioned above.

  2. Use react-native run-android or react-native run-ios to rebuild and run your app. Specify custom paths via custom arguments..

  3. Attention: Whenever you change your configuration in dynatrace.config.js please use react-native start --reset-cache option. Metro caches all files and a configuration change might lead to a different situation. Not resetting the cache might result in an mixture of old and new configuration.

Advanced topics

Manual OneAgent startup

If you can't do a automated startup through the dynatrace.config.js, you can always perform a manual startup and decide values such as beaconUrl and applicationId at runtime.

Note: An automated startup usually provides you with a lifecycle application start-up event. A manual startup on the other hand occurs later, thereby causing you to miss everything, including this application startup event, until the startup occurs.

A manual startup requires the following two steps:

  1. Deactivate the automated startup in dynatrace.config.js:
module.exports = {
    react: {
        autoStart: false,
        ...
    },
    android: {
        config: `
        dynatrace {
            configurations {
                defaultConfig {
                    autoStart.enabled false
                }
            }
        }
        `
    },
    ios: {
        config: `
        <key>DTXAutoStart</key>
        <false/>
        `
    }
}
  1. Make the start-up call with at least beaconUrl and applicationId:

Example of a startup call:

import { Dynatrace, ManualStartupConfiguration } from '@dynatrace/react-native-plugin';

Dynatrace.start(new ManualStartupConfiguration("beaconUrl", "applicationId"));

For more details see the section about startup API.

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.

Manual instrumentation

To use the API of the React Native plugin, import the API:

import { Dynatrace, Platform } from '@dynatrace/react-native-plugin';

Plugin startup

The manual startup of the plugin is triggered via the start(configuration: ManualStartupConfiguration) 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, thhere are several optional configuration parameters, which are shown in the table below:

new ManualStartupConfiguration(
  beaconUrl,
  applicationId,
  reportCrash: true,
  logLevel: LogLevel.Info,
  lifecycleUpdate: false,
  certificateValidation: true;
  userOptIn: false;
)
Property name Type Default Description
beaconUrl string null Identifies your environment within Dynatrace. This property is mandatory for manual startup. OneAgent issues an error when the key isn't present.
applicationId string null Identifies your mobile app. This property is mandatory for manual startup. OneAgent issues an error when the key isn't present.
reportCrash boolean true Reports crashes.
logLevel LogLevel LogLevel.Info Allows you to choose between LogLevel.Info and LogLevel.Debug. Debug returns more logs. This is especially important when something is not functioning correctly.
lifecycleUpdate boolean false Decide if you want to see update cycles on lifecycle actions as well. This is per default false as it creates a lot more actions.
certificateValidation boolean true Allows the use of self-signed certificates. By default, it is set to false. When set to true, OneAgent accepts self-signed certificates that are not signed by a root-CA. This configuration key doesn't impact mobile app connections. It's only used for OneAgent communication, but doesn't overrule the host-name validation.
userOptIn boolean false Activates the privacy mode when set to true. User consent must be queried and set. The privacy settings for data collection and crash reporting can be changed via OneAgent SDK for Mobile as described under Data privacy. The default value is false.

Note: The values used for the parameters are their default value.

Attention: 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. You will find a list which explains all the counterparts for the available options here.

Monitor a Component

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.

  • Example with Functional Component:
export function MyFunctionalComponent(){
  ...
}

Dynatrace.withMonitoring(MyFunctionalComponent, "MyFunctionalComponent");

The String "MyFunctionalComponent" is optional as the name of the component can be retrieved through different properties.

Combining manual and auto instrumentation is not creating a problem as both are executing the same thing. Manual instrumentation would only override the content of auto instrumentation happening through the transformer.

Create manual actions

To create a manual 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.

let myAction = Dynatrace.enterAction("MyButton tapped");
//Perform the action and whatever else is needed.
myAction.leaveAction();

Create manual sub actions

You can create a single manual 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.

let myAction = Dynatrace.enterAction("MyButton tapped");
let mySubAction = Dynatrace.enterAction("MyButton Sub Action");
//Perform the action and whatever else is needed.
mySubAction.leaveAction();
myAction.leaveAction();

Cancel actions

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.

let myAction = Dynatrace.enterAction("MyButton tapped");
// Action will be canceled
myAction.cancel();

// Has no impact as the action is already canceled
myAction.leaveAction();

Report values

For any open action you can report certain values. The following API is available for action:

reportDoubleValue(valueName: string, value: number, platform?: Platform): void
reportError(errorName: string, errorCode: number, platform?: Platform): void
reportEvent(eventName: string, platform?: Platform): void
reportIntValue(valueName: string, value: number, platform?: Platform): void
reportStringValue(valueName: string, value: string, platform?: Platform): void

To report a string value, use the following:

let myAction = Dynatrace.enterAction("MyButton tapped");
myAction.reportStringValue("ValueName", "ImportantValue");
myAction.leaveAction();

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.

Report an error stacktrace

To manually report an error stacktrace, use the following API call:

reportErrorWithStacktrace(errorName: string, errorValue: string, reason: string, stacktrace: string, platform?: Platform, errorValue?: string): void;

Note: The previous API without errorValue is deprecated and will be removed in the future. Please use the new API with errorValue if possible.

Identify a user

You can identify a user and tag the current session with a name by making the following call:

Dynatrace.identifyUser("User XY");

Manually report a crash

You can manually report a crash via the following API call:

reportCrash(crashName: string, reason: string, stacktrace: string, platform?: Platform): void;
reportCrashWithException(crashName: string, crash: Error, platform?: Platform): void;

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.

reportCrashWithException will use the crashName as name for the crash. It will only report the crash if there is also a stacktrace available.

User Privacy Options

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:

export enum DataCollectionLevel {
    Off, Performance, UserBehavior
}
  1. Off: Native Agent doesn't capture any monitoring data.
  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.
  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.

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 and set the User Privacy Options.

The API to get and set the current privacy level looks like this:

async getUserPrivacyOptions(platform?: Platform): Promise<UserPrivacyOptions>;
applyUserPrivacyOptions(userPrivacyOptions: UserPrivacyOptions, platform?: Platform): void;

To check the current privacy options that are set:

let privacyOptions = await Dynatrace.getUserPrivacyOptions();

If you want to create a new UserPrivacyOptions object:

let privacyConfig = new UserPrivacyOptions(DataCollectionLevel.UserBehavior, true);

To set new values to this object:

privacyConfig.crashReportingOptedIn = false;
privacyConfig.dataCollectionLevel = DataCollectionLevel.Performance;

The properties that are used to set the privacy options can also be used to fetch the options:

let level = privacyConfig.dataCollectionLevel;
let crashReporting = privacyConfig.crashReportingOptedIn;

To apply the values that were set on the object:

Dynatrace.applyUserPrivacyOptions(privacyConfig);

Report GPS Location

You can report latitude and longitude and specify an optional platform.

setGPSLocation(latitude: number, longitude: number, platform?: Platform): void

Platform independent reporting

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:

  • Passing to iOS only:
let myAction = Dynatrace.enterAction("MyButton tapped", Platform.Ios);
//Perform the action and whatever else is needed.
myAction.leaveAction("ios"); 
  • Passing to Android only:
let myAction = Dynatrace.enterAction("MyButton tapped", Platform.Android);
//Perform the action and whatever else is needed.
myAction.leaveAction("android"); 
  • Passing to both:
let myAction = Dynatrace.enterAction("MyButton tapped");
//Perform the action and whatever else is needed.
myAction.leaveAction(); 

Setting beacon headers

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.

Note: To clear the previously set headers, call the method without the headers parameter or with a null value for the headers parameter.

setBeaconHeaders(headers?: Map<string, string> | null, platform?: Platform): void;

Exclude Individual JSX Elements

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:

function TouchableHighlightScreen() {
    return (
        <View>
            <TouchableHighlight onPress={onPress}>
                <Text>TouchableHighlight that will be monitored</Text>
            </TouchableHighlight>
            <TouchableHighlight onPress={onPress} dtActionIgnore="true">
                <Text>TouchableHighlight that will be ignored</Text>
            </TouchableHighlight>
        </View>
    );
}

function onPress() {
    Logger.logDebug("TouchableHighlight Pressed!");
}

export default TouchableHighlightScreen;

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.

If you want to set this property and use typescript you can use the following workaround:

<TouchableHighlight {...{ "dtActionIgnore": "true" }}><Text>TouchableHighlight that will be ignored</Text></TouchableHighlight>

Custom arguments for instrumentation script

Note: This feature works directly on run-android, run-ios or start command only for RN 60.1 or higher.

MacOS Users: This feature is not working correctly on MacOS. Arguments are not passed between run-ios and starting the webserver. If you still want to use custom arguments you need to start the webserver first with custom arguments and later on executing run-ios, which will then no longer create a webserver as it is already running in background.

Our scripts assumes that the usual React Native project structure is given. The following arguments can be specified for our instrumentation script if the project structure is different.

  • gradle=C:\MyReactAndroidProject\build.gradle: The location of the root build.gradle file. We will assume that the other gradle file resides in /app/build.gradle. This will add the whole agent dependencies automatically for you and will update the configuration.
  • plist=C:\MyReactIOSProject\projectName\info.plist: Tell the script where your info.plist file is. The plist file is used for updating the configuration for the agent.
  • config=C:\SpecialFolderForDynatrace\dynatrace.config.js: If you have not got your config file in the root folder of the React Native project but somewhere else.

Examples:

  • RN 60.1:
react-native run-android config=C:\SpecialFolderForDynatrace\dynatrace.config.js --port=2000

Note: that custom arguments must not be prefixed with -- !

Manually adding iOS OneAgent to a project

Adding the iOS agent manually depends on the availabilty of support for CocoaPods.

With CocoaPods support

Insert the following in your Podfile:

pod 'react-native-dynatrace', :path => '../node_modules/@dynatrace/react-native-plugin'

Without CocoaPods support

  1. Open your project in Xcode.
  2. Run open node_modules/@dynatrace/react-native-plugin/ios.
  3. Drag DynatraceRNBridge.xcodeproj into your Libraries group.
  4. Select your main project in the navigator to bring up settings.
  5. Under Build Phases expand the Link Binary With Libraries header.
  6. Scroll down and click + to add a library.
  7. Find and add libRNDynatrace.a under the Workspace group.
  8. ⌘+B

Structure of the dynatrace.js file

The configuration is structured in the following way:

module.exports = {
    react : {
      // Configuration for React Native instrumentation
    },
    android : {
      // Configuration for Android auto instrumentation
    },
    ios : {
      // Configuration for iOS auto instrumentation
    }
}

Manual Startup Counterparts

Here is a list of all the counterparts for the options that can be used with a manual startup. Below the counterparts table you will find an example configuration block for both Android and iOS.

Property Name Default Android iOS React
beaconUrl null autoStart.beaconUrl DTXBeaconURL -
applicationId null autoStart.applicationId DTXApplicationId -
reportCrash true crashReporting DTXCrashReporting -
logLevel LogLevel.Info debug.agentLogging DTXLogLevel debug
lifecycleUpdate false - - lifecycle.includeUpdate
certificateValidation false debug.certificateValidation DTXAllowAnyCert -
userOptIn false userOptIn DTXUserOptIn -

React block

The react configuration block contains all settings regarding the react instrumentation. The following options are available:

Input

react : {
  input : {
    instrument(filename) => {
      return true;
    }
  }
}

This instrument function expects you to return true or false. In this case, all files are instrumented to capture user input.

Lifecycle

react : {
  lifecycle : {
    includeUpdate : false,
    instrument(filename) => {
      // This will only capture inputs in files in the path src/screens/
      return filename.startsWith(require('path').join('src', 'screens'));
    }
  }
}

The instrument function expects you to return true or false. In this case, all files in the src/screens/ folder are instrumented to capture user input.

Note: it is important that you input all files here where you wish lifecycle instrumentation. Probably this should contain all your "real" screens. If you return true for this function, all lifecycle events will be reported, which can be a lot in React Native.

Debug mode

react: {
  debug: true
}

This activates the debug mode. You will get more console output during instrumentation and at runtime.

Autostart

react: {
  autoStart: true
}

This activates the AutoStart mode, which will insert an auto start call in your React Native application. This is per default true. If you want to use a manual startup call, please have a look into the manual startup section.

Android block

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:

android : {
  config : `CONTENT_OF_ANDROID_CONFIG`
}

The content of the config block is directly copied to the Gradle file. To know more about the possible configuration options, see the DSL documentation of our Gradle plugin.

iOS block

The iOS block is a wrapper for the iOS configuration you find in the WebUI (in the Mobile Application Settings). Copy the content into the following block:

ios : {
  config : `CONTENT_OF_IOS_CONFIG`
}

The content of the config block is directly copied to the plist file. Therefore, you can use every setting that is possible and you find in the official Mobile Agent documentation.

Define build stages in dynatrace.config.js

If you have several stages such as debug, QA, and production, you probably want to seperate them and let them report in different applications. This can be done with two different approaches:

  1. Create several dynatrace.config.js (e.g. dynatrace.config.prod.js) and pass those configuration files via arguments in the CLI.
  2. Use the configuration options which are available through Gradle and XCode. (Described below)

Note: Option 1 has the drawback that you always need to perform the configuration step before a build as you are basically replacing the configuration all the time. So if you made a debug build and want to do a production build, which is reporting to a different environment or has different options, you need to perform npm run instrumentDynatrace (Or if you use RN 0.60+ this happens automatically with react-native run-android or react-native run-ios).

Android

In Android, you can enter all the information in the config file. The following dynatrace {} block must be inserted into the android config variable in your dynatrace.config.js file.

android : {
  config : `
    dynatrace {
      configurations {
        dev {
            variantFilter "Debug" // build type name is upper case because a product flavor is used
            // other variant-specific properties
        }
        demo {
            variantFilter "demo" // the first product flavor name is always lower case
            // other variant-specific properties
        }
        prod {
            variantFilter "Release" // build type name is upper case because a product flavor is used
            // other variant-specific properties
        }
      }
    }
  `
}

This will result in the following:

> Task :app:printVariantAffiliation
Variant 'demoDebug' will use configuration 'dev'
Variant 'demoRelease' will use configuration 'demo'
Variant 'paidDebug' will use configuration 'dev'
Variant 'paidRelease' will use configuration 'prod'

In all these blocks, you can define different application IDs and even use a different environment.

iOS

In iOS, you can define some variables in the dynatrace.config.js file. These variables must then be inserted in a prebuild script. The following properties must be inserted into the iOS config variable in your dynatrace.config.js file.

ios: {
  config: `
  <key>DTXApplicationID</key>
  <string>\${APPLICATION_ID}</string>
  <key>DTXBeaconURL</key>
  <string>Your Beacon URL</string>
  `
}

The variable ${APPLICATION_ID} must then be inserted with a prebuild script. Make sure to use \ in front of the ${...}, because if not JavaScript thinks you are trying to insert a variable into the String. For more information, see https://medium.com/@andersongusmao/xcode-targets-with-multiples-build-configuration-90a575ddc687.

User opt-in mode

Specifies if the user has to opt-in for being monitored. When enabled, you must specify the privacy setting. For more information, see the API section.

Android

android: {
  config: `
    dynatrace {
      configurations {
        defaultConfig {
          autoStart{
            ...
          }
          userOptIn true
        }
      }
    }
  `
}

iOS

ios: {
  config: `
  <key>DTXUserOptIn</key>
  </true>
  `
}

Native OneAgent debug logs

If the instrumentation runs through and your application starts but you see no data, you probably need to dig deeper to find out why the OneAgents aren't sending any data. Opening up a support ticket is a great idea, but gathering logs first is even better.

Android

Add the following configuration snippet to your other configuration in dynatrace.config.js right under the autoStart block (the whole structure is visible, so you know where the config belongs) and run npm run instrumentDynatrace:

android: {
  config: `
    dynatrace {
      configurations {
        defaultConfig {
          autoStart{
            ...
          }
          debug.agentLogging true
        }
      }
    }
  `
}

iOS

Add the following configuration snippet to your other configuration in dynatrace.config.js (the whole structure is visible, so you know where the config belongs) and run npm run instrumentDynatrace:

ios: {
  config: `
  <key>DTXLogLevel</key>
  <string>ALL</string>
  `
}

How does Dynatrace determine the user action name?

  • React views
    • dtActionName: Use a custom property called dtActionName
    • displayName: Use the displayName property to check if React views have a display name set (Not available for functional component)
    • instrumentation string: Auto instrumentation or manual instrumentation is passing a string. This will be used if available.
    • class name: If the display name is not available, the class name is used by taking the property name from the constructor
  • Touchables
    • dtActionName: Use a custom property called dtActionName
    • Accessibility label
    • If both are not set, it will search for an inner text
    • If it is an Image Button, it will search for a source
  • Buttons
    • dtActionName: Use a custom property called dtActionName
    • Button Title
    • Accessibility label
    • If it is an Image Button, it will search for a source
    • If it finds nothing, it will search for an inner text

Attention: Minification can cause a loss of information.

Using dtActionName to change the name of the action

We check for a property named dtActionName when creating an action. If dtActionName exists, this will be used for the action name above every other option listed in the previous section. Examples:

Typescript:

<TouchableHighlight {...{ "dtActionName": "CustomActionName" }}><Text>Custom Action Name</Text></TouchableHighlight>

JavaScript:

<TouchableHighlight dtActionName="CustomActionName"><Text>Custom Action Name</Text></TouchableHighlight>

React Automatic Runtime

React introduced with React 17.x, React 16.14.0, React 15.7.0, and React 0.14.10 the automatic runtime which changes the JSX transformation (https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html).

This impacts our instrumentation as well. To keep the instrumentation in place you need to change your babel configuration. Currently the configuration might look like the following or similiar:

{
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "runtime": "automatic"
      }
    ]
  ]
}

To still support our instrumentation you need to add the importSource property. This change applied to the snippet above, will look the following:

{
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "runtime": "automatic", 
        "importSource": "@dynatrace/react-native-plugin/lib"
      }
    ]
  ]
}

Using a second transformer besides the dynatrace transformer

If you want to register the Dynatrace transformer in your configuration and you already have a transformer in place, change the upstreaming transformer for the Dynatrace transformer.

This can be done via a configuration value in the dynatrace.config.js :

// The `...` only indicates that there are other values as well, but we've omitted them in this example.
module.exports = {
    react : {
        upstreamTransformer: require.resolve('customTransformerLib/myTransformer'),
        ...
    },
    ...       
}

Maven Central in top-level gradle file

Because the Dynatrace Android agent now requires the MavenCentral repository, if either jcenter() or mavenCentral() is not added inside of ALL the repositories blocks via the top-level build.gradle, the build will fail. Below is an example of what a basic top-level build.gradle file should look like after adding mavenCentral() to all repository blocks:

mavenCentralRN

The location of the top-level build.gradle should be:

  • <rootOfProject>\android\build.gradle

Note: JCenter has noted its sunset on May 1st. Though, JCenter is still syncing with Maven Central so havingjcenter() in your build.gradle file without the use of mavenCentral() will retrieve the Dynatrace Android Gradle Plugin no problem.

Updating to Gradle 5

Updating Gradle only affects your Android build. To update your project to Gradle 5, modify the following 3 files in your Android folder.

  • ProjectFolder\android\gradle\wrapper\gradle-wrapper.properties Update the distributionUrl to get a higher gradle version.
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
  • ProjectFolder\android\build.gradle Update the version of your Android gradle plugin (here we updated to 3.4.0) as Gradle 5 needs a higher one. To get the newer versions, add google() in your repositories. Example of a build.gradle file:
buildscript {
    repositories {
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.0'
    }
}

allprojects {
    repositories {
        google()
        mavenLocal()
        jcenter()
        maven {
            url "$rootDir/../node_modules/react-native/android"
        }
    }
}
  • ProjectFolder\android\app\build.gradle This depends on how old your React Native project really is. You must change your used buildTools, compileSdkVersion, targetSdkVersion and support libaries. Older build.gradle files might look similar to this with unimportant parts removed to make the snippet smaller:
...

apply from: "../../node_modules/react-native/react.gradle"

...

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 28

        ...
    }

    ...
}

dependencies {
    compile "com.android.support:appcompat-v7:28.0.0"
    compile "com.facebook.react:react-native:+" 
}

...

Configuration of standalone React Native project

This section explains the configuration of a standalone React Native project. This means you have a React Native project, but don't use the typicial iOS and android folders. Instead you have a seperate native iOS or android project which is embedding your React Native project.

To get the same experience as somebody who has a combined project, you roughly need to do the following things:

  • Apply Auto Instrumentation to your Native Project
  • Add this plugin to your React Native project

Auto Instrumentation of your Native Project

The mobile application in the web UI offers you a configuration wizard (see settings page) for your native project (Android/iOS). Use it and apply it to your seperated native project according to this documentation:

Add this plugin to your React Native project

After you have added the auto instrumentation to your native project, you need to add this plugin to your standalone react native project.

You can simply follow the setup shown in the beginning of this documentation. There is no special handling needed, except skipping step 4, as it is of course not needed as you usually create a bundle when building a standalone project.

  • Optional: You can remove the android and ios block from your dynatrace.config.js. It has no impact on the plugin as the configuration of the native platforms is skipped because of the missing iOS and android folder.

Dynatrace documentation

The OneAgent for Android and iOS documentation is available at the following locations:

Note: The Dynatrace Android Gradle plugin is hosted on Maven Central. JCenter has noted it's sunset on May 1st so Maven Central is the primary source of the Dynatrace Android Gradle plugin.

Troubleshooting and applicable restrictions

Attention: If you think something is not working the way it should, ALWAYS try to reset the cache of metro first before starting a support case. You can do this via the CLI react-native start --reset-cache. If it still does not work feel free to open a support case.

To resolve problems with the plugin, first look at creating logs and identify what went wrong. The logs can be found in the plugins folder of your React Native project (usually node_modules/@dynatrace/react-native-plugin/logs).

  • An error such as DynatraceNative.PLATFORM_ANDROID is null indicates that the linking of the native library didn't work correctly. Often, React Native is unable to link correctly. Simply unlink (react-native unlink) and link (react-native link) again and the error should be gone.
  • Missing property DTXApplicationID indicates that there is no configuration available. Ensure that you've called npm run updateConfiguration at least once.
  • If you change your project to pods when you have already installed the plugin, duplicate symbols are generated because of the already linked library. Remove the module reference manually from your project.
  • Build failes with the error of "No configuration for the Dynatrace Android Gradle plugin found! Please copy the configuration block from the instrumentation wizard to the proper location." ** The android configuration was not added to your project. Please refer to the install the plugin section.
  • For Android, if you see an error like "Gradle sync failed: Could not find com.dynatrace.tools.android:gradle-plugin:8.223.1.1003.", please see the MavenCentral section for an example and more information.

Report a bug or open a support case

Attention: If you think something is not working the way it should, ALWAYS try to reset the cache of metro first before starting a support case. You can do this via the CLI react-native start --reset-cache. If it still does not work feel free to open a support case.

If you are struggling with a problem, submit a support ticket to Dynatrace (support.dynatrace.com) and provide the following details:

  • Logs from the native agent
  • Logs from node_modules/@dynatrace/react-native-plugin/logs
  • Your dynatrace.config.js file

Changelog

2.231.1

  • Added cancel API for actions
  • Updated Android Agent (8.231.2.1007) & iOS Agent (8.231.1.1009)
  • React Native 0.66.0 compatibility

2.227.0

  • Fixed auto start in React Native 0.65.x
  • Updated Android Agent (8.225.1.1004)

2.225.0

  • Custom config argument fixed for transformer
  • Fixed infite loop of HOC with React Redux & Functional component
  • Added Manual API for Class & Function component instrumentation
  • Internally changed way of class component instrumentation
  • Updated Android Agent (8.223.1.1003) & iOS Agent (8.223.1.1006)

2.217.0

2.214.0

  • Fixed runtime exchange of functional component

2.207.1

1.205.0

  • Fixed transformation problems with TSImportType and JSX
  • Updated error handler to report crashes instead of error stacktraces
  • Added setBeaconHeaders API
  • Added UserPrivacyOptions API
  • Updated DataCollection level enum members to match native agents

1.202.0

  • Startup configuration now only written once
  • Fixed usage of displayName for component action creation

1.201.0

  • Improved exception handling

1.200.0

  • .TSX transformation fixed

1.198.0

  • Podspec Update

1.192.2

  • Fix for Installation Script not executed
  • RN >= 0.62 Touchables support
  • Multi-Transformer support
  • Updated & fixed .d.ts file
  • Fixed NPE in text identification of Touchables
  • Crashes are now reported in the overview
  • iOS Webrequests are now linked with actions
  • Fix for custom arguments on run-ios and run-android

0.186.0

  • Fixed instrumentation (files were skipped)
  • Changed Configuration format
  • Android: Switched to JCenter repository
  • Applying configuration automatically (>= RN 0.60)
  • Updated documenation for manual instrumentation
  • Fixed problem with default config and beaconUrl
  • Improved text identification of Touchables
  • ImageButtons and Icons will now be reported
  • Improved logic for plist file insertion

0.182.2

  • MacOS: Fixed directory creation issue

0.182.1

  • Fixed Typescript Parsing
  • Fixed Decorator Parsing
  • Fixed directory issue with older node version

0.181.1

  • Picker & Swipe to Refresh instrumented
  • Dynamic Imports/Requires now supported
  • Fixed iOS Bug with reportErrorWithStacktrace

0.179.1

  • Made Plugin compatible with RN AutoLinking
  • Improved instrumentation of require & imports
  • Fixed Button instrumentation
  • Improved Text identification of Touchable
  • Webrequest linking (Android only)
  • Auto User action creation (Android only)
  • Report Stacktrace via Error API (Android & iOS)
  • Uninstall process now removes everything
  • Modifying SourceMap, Debugging now possible
  • Fixed configuration issue with npm install

0.174.0

  • Switching to new Android instrumentation
  • Added options to filter instrumentation

0.172.0

  • Error reporting through auto instrumentation
  • Debug message output in console

0.171.0

  • Added auto instrumentation for React classes

0.168.0

  • Initial Beta Release