Monitor Hybrid React Native Applications
このページは日本語には対応しておりません。随時翻訳に取り組んでいます。翻訳に関してご質問やご意見ございましたら、お気軽にご連絡ください。
Overview
React Native is a JavaScript framework for developing hybrid mobile applications that can run natively on both Android and iOS.
If you have a hybrid application that is built on React Native, you can use Datadog to monitor the same application from both the native Android or iOS side and the React Native side.
RUM Events from both of these sources are reported as coming from the same application and the same source in Datadog RUM.
Limitations
- For errors, resources, and interactions tracking, the SDKs can work in the following ways:
- Through auto-instrumentation - Some React classes and methods are modified to automate this. Auto-instrumentation for JavaScript errors, resources, and interactions can only be started from JavaScript code.
- Through manual instrumentation - For example, if you want to report something that you consider an error, but which is not going to crash the app.
- You can share the same instance of the core SDK between native and React Native without having to initialize the SDK on both sides separately. This allows you to initialize the native SDK on either the native side or on the React Native side (by calling
DdSdkReactNative.initialize
) and have it initialized for both sides, with events appearing in the same RUM session. React Native uses the default core instance. This means that you can use manual instrumentation on both sides, but auto-instrumentation is only activated for the side that the SDK was initialized. - You can report Datadog RUM events or logs only after the initialization. If you have not initialized the SDK yet, events and legs are not sent.
- You cannot change the source attribute of a RUM session - all your RUM events appear under the same source.
Monitor React Native apps with native content
Before you can start monitoring Reactive Native apps with native content, you need to initialize the React Native SDK.
Initialize the React Native SDK
In order to initialize the SDK on both the React Native and the native side, you can follow the React Native Monitoring documentation.
With this setup, you can call both the native and React Native SDKs for logs, traces and RUM.
We recommend this solution if you haven’t used our SDK on the native side before you initialize it on the React Native side.
On Android, add the Datadog Android SDKs to your dependencies in your android/app/build.gradle
file:
// The version is set by @datadog/mobile-react-native
implementation "com.datadoghq:dd-sdk-android-rum"
implementation "com.datadoghq:dd-sdk-android-logs"
implementation "com.datadoghq:dd-sdk-android-trace"
implementation "com.datadoghq:dd-sdk-android-webview"
On iOS, add the Datadog iOS SDKs to your dependencies in your ios/Podfile to use in Objective C files:
# Make sure the version matches the one from node_modules/@datadog/mobile-react-native/DatadogSDKReactNative.podspec
pod 'DatadogSDKObjc', '~> 2.5.0'
Tracking native RUM views
If you use a navigation library for your React Native app like react-navigation
, using the nativeViewTracking
configuration option creates many duplicate views.
If this is the case, track your native RUM Views manually. See documentation for iOS and for Android.
Tracking native RUM resources
If you have enabled tracing with your backend, first-party hosts for your native RUM Resources are the same as for your React Native RUM resources.
Limitations
If you write any native code that relies on the Datadog SDK, make sure you execute that code after initializing the SDK on the React Native side. When you initialize the SDK on the React Native side, it is also initialized on the native side.
Monitor native apps with React Native screens
Before you can start monitoring Reactive Native apps with native content, you need to initialize the React Native SDK.
Initialize the React Native SDK
Install the React Native Datadog SDK with the following command options:
yarn add @datadog/mobile-react-native
or
npm install @datadog/mobile-react-native
Add the Datadog Android SDK to your dependencies in your android/app/build.gradle
file:
// The version is set by @datadog/mobile-react-native
implementation "com.datadoghq:dd-sdk-android-rum"
implementation "com.datadoghq:dd-sdk-android-logs"
implementation "com.datadoghq:dd-sdk-android-trace"
implementation "com.datadoghq:dd-sdk-android-webview"
Initialize the SDK on the native side. See the official Android documentation for instructions.
Initialize the SDK on the native side. See the official iOS documentation for instructions.
Instrumenting React Native RUM Views
Use a ComponentPredicate
to filter out native views that are created by your navigation libraries:
// Adapt the Fragment type to your View tracking strategy
class RNComponentPredicate : ComponentPredicate<Fragment> {
override fun accept(component: Fragment): Boolean {
// Identify and drop react native screen views
if (component.javaClass.name.startsWith("com.swmansion.rnscreens")) {
return false
}
if (component.javaClass.name.startsWith("com.facebook.react")) {
return false
}
return true
}
override fun getViewName(component: Fragment): String? {
return null
}
}
// Use it in your RUM configuration
rumConfiguration.useViewTrackingStrategy(FragmentViewTrackingStrategy(true, RNComponentPredicate()))
Then, use @datadog/mobile-react-navigation
to track your views.
If you have enabled ProGuard obfuscation, add rules to prevent obfuscation of the target packages in release builds.
Use a UIKitRUMViewsPredicate
to filter out native views that are created by your navigation libraries:
class RNHybridPredicate: UIKitRUMViewsPredicate {
var defaultPredicate = DefaultUIKitRUMViewsPredicate()
func rumView(for viewController: UIViewController) -> RUMView? {
let canonicalClassName = NSStringFromClass(type(of: viewController))
// Dropping RN Views
if (canonicalClassName.starts(with: "RN")) {
return nil
}
return defaultPredicate.rumView(for: viewController)
}
}
// Use it in your RUM configuration
let rumConfiguration = RUM.Configuration(
applicationID: applicationId,
uiKitViewsPredicate: RNHybridPredicate(),
)
Instrumenting React Native errors, interactions, and resources
Wrap your React Native app with the DatadogProvider
component to automatically register React Native RUM errors, interactions, and resources:
const configuration = {
trackResources: true,
trackErrors: true,
trackInteractions: true
};
const RNApp = props => {
useEffect(() => {
/**
* In here we can put fake values. The only goal of this call
* is to empty the buffer of RUM events.
*/
DatadogProvider.initialize({
clientToken: 'fake_value',
env: 'fake_value',
applicationId: 'fake_value'
});
}, []);
const navigationRef = useRef(null);
return (
<DatadogProvider configuration={configuration}>
{/* Content of your app goes here */}
</DatadogProvider>
);
};
AppRegistry.registerComponent('RNApp', () => RNApp);
To remove duplicated interactions on Android, filter out the React Native interactions on the native side with an EventMapper:
class RNActionEventMapper : EventMapper<ActionEvent> {
override fun map(event: ActionEvent): ActionEvent? {
var targetClassName = (event.context?.additionalProperties?.get("action.target.classname") as? String)
if(targetClassName?.startsWith("com.facebook.react") == true) {
return null
}
return event
}
}
// Use it in your RUM configuration
rumConfiguration.setActionEventMapper(RNActionEventMapper())
If you have enabled ProGuard obfuscation, add rules to prevent obfuscation of the target packages in release builds.
Limitations
If you specified a resourceEventMapper
or actionEventMapper
in your React Native configuration, resources and actions won’t be dropped if you return null
in the mapper.
To keep this functionality, add the following snippets in your native configuration for your platform:
val config = RumConfiguration.Builder(applicationId = appId)
.setResourceEventMapper(object : EventMapper<ResourceEvent> {
override fun map(event: ResourceEvent): ResourceEvent? {
if (event.context?.additionalProperties?.containsKey("_dd.resource.drop_resource") == true) {
return null
}
// You can add your custom event mapper logic here
return event
}
})
.setActionEventMapper(object : EventMapper<ActionEvent> {
override fun map(event: ActionEvent): ActionEvent? {
if (event.context?.additionalProperties?.containsKey("_dd.action.drop_action") == true) {
return null
}
// You can add your custom event mapper logic here
return event
}
})
RUM.Configuration(
applicationID: applicationId,
resourceEventMapper: { resourceEvent in
if resourceEvent.context?.contextInfo["_dd.resource.drop_resource"] != nil {
return nil
}
// You can add your custom event mapper logic here
return resourceEvent
},
actionEventMapper: { actionEvent in
if actionEvent.context?.contextInfo["_dd.resource.drop_action"] != nil {
return nil
}
// You can add your custom event mapper logic here
return resourceEvent
}
)
Further Reading