ハイブリッド React Native アプリケーションのモニタリング
概要
React Native は、Android と iOS の両方でネイティブに動作するハイブリッドモバイルアプリケーションを開発できる JavaScript フレームワークです。
ハイブリッドアプリケーションが React Native で構築されている場合、Datadog を使用して、同じアプリケーションをネイティブ (Android または iOS) と React Native の両方の側面からモニタリングできます。
これら両方のソースからの RUM イベントは、Datadog RUM 上では同じアプリケーション・同じソースからのイベントとして報告されます。
制限
- エラー、リソース、インタラクションのトラッキングに関して、SDK は次の方法で動作できます。
- 自動インスツルメンテーション (auto-instrumentation): 一部の React クラスやメソッドを修正し、自動化を行います。JavaScript のエラー、リソース、インタラクションの自動インスツルメンテーションは、JavaScript コードからのみ開始できます。
- 手動インスツルメンテーション (manual instrumentation): たとえば、アプリがクラッシュはしないもののエラーだとみなしたい事象を報告したい場合などに使用します。
- コア SDK の同じインスタンスを、ネイティブ側と React Native 側の両方で共有でき、両側で別々に初期化する必要はありません。これにより、ネイティブ側でネイティブ SDK を初期化するか、React Native 側で
DdSdkReactNative.initialize
を呼び出すことで初期化を行うかにかかわらず、どちらでも SDK が初期化され、同じ RUM セッションにイベントが表示されます。React Native はデフォルトのコアインスタンスを使用します。つまり、両側で手動インスツルメンテーションを使用できますが、自動インスツルメンテーションは SDK を初期化した側でのみ有効になります。 - Datadog RUM イベントやログは、初期化後にのみ報告できます。まだ SDK を初期化していない場合、イベントやログは送信されません。
- RUM セッションのソース属性は変更できず、すべての RUM イベントは同じソース下に表示されます。
ネイティブコンテンツを含む React Native アプリのモニタリング
ネイティブコンテンツを含む React Native アプリをモニタリングする前に、React Native SDK を初期化する必要があります。
React Native SDK の初期化
React Native とネイティブの両方で SDK を初期化するには、React Native Monitoring のドキュメントを参照してください。
このセットアップにより、ログ、トレース、RUM をネイティブ SDK と React Native SDK の両方で呼び出すことができます。
React Native 側で初めて SDK を初期化する前に、ネイティブ側で Datadog SDK を使用したことがない場合は、このソリューションを推奨します。
Android では、android/app/build.gradle
ファイルの依存関係に Datadog Android SDK を追加します。
// バージョンは @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"
iOS では、Objective C ファイルで使用するために ios/Podfile の依存関係に Datadog iOS SDK を追加します。
# バージョンが node_modules/@datadog/mobile-react-native/DatadogSDKReactNative.podspec のものと一致していることを確認してください
pod 'DatadogSDKObjc', '~> 2.5.0'
ネイティブ RUM ビューのトラッキング
react-navigation
などのナビゲーションライブラリを使用して React Native アプリを開発している場合、nativeViewTracking
構成オプションを使用すると、重複するビューが多数作成されることがあります。
その場合は、ネイティブ RUM ビューを手動でトラッキングしてください。iOS および Android のドキュメントを参照してください。
ネイティブ RUM リソースのトラッキング
バックエンドとのトレースを有効にしている場合、ネイティブ RUM リソースのファーストパーティホストは、React Native RUM リソースと同じです。
制限
ネイティブコードで Datadog SDK に依存する部分がある場合は、そのコードを React Native 側で SDK を初期化した後に実行するようにしてください。React Native 側で SDK を初期化すると、ネイティブ側でも SDK が初期化されます。
ネイティブアプリを React Native スクリーンと一緒にモニタリングする
ネイティブコンテンツを含む React Native アプリをモニタリングする前に、React Native SDK を初期化する必要があります。
React Native SDK の初期化
以下のコマンドオプションを使用して React Native Datadog SDK をインストールします。
yarn add @datadog/mobile-react-native
または
npm install @datadog/mobile-react-native
android/app/build.gradle
ファイルの依存関係に Datadog Android SDK を追加します。
// バージョンは @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"
ネイティブ側で SDK を初期化します。手順については公式 Android ドキュメントを参照してください。
ネイティブ側で SDK を初期化します。手順については公式 iOS ドキュメントを参照してください。
React Native RUM ビューのインスツルメンテーション
ナビゲーションライブラリによって作成されるネイティブビューを除外するには、ComponentPredicate
を使用します。
// ビュートラッキングの戦略に合わせて Fragment の型を調整してください
class RNComponentPredicate : ComponentPredicate<Fragment> {
override fun accept(component: Fragment): Boolean {
// React Native のスクリーンビューを除外
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
}
}
// RUM 設定で使用します
rumConfiguration.useViewTrackingStrategy(FragmentViewTrackingStrategy(true, RNComponentPredicate()))
その後、@datadog/mobile-react-navigation
を使用してビューをトラッキングします。
ProGuard の難読化を有効にしている場合は、リリースビルドでターゲットパッケージが難読化されないようにルールを追加してください。
ナビゲーションライブラリによって作成されるネイティブビューを除外するため、UIKitRUMViewsPredicate
を使用します。
class RNHybridPredicate: UIKitRUMViewsPredicate {
var defaultPredicate = DefaultUIKitRUMViewsPredicate()
func rumView(for viewController: UIViewController) -> RUMView? {
let canonicalClassName = NSStringFromClass(type(of: viewController))
// RN Views を除外
if (canonicalClassName.starts(with: "RN")) {
return nil
}
return defaultPredicate.rumView(for: viewController)
}
}
// RUM 設定で使用します
let rumConfiguration = RUM.Configuration(
applicationID: applicationId,
uiKitViewsPredicate: RNHybridPredicate(),
)
React Native エラー、インタラクション、リソースのインスツルメンテーション
DatadogProvider
コンポーネントで React Native アプリをラップすると、React Native RUM のエラー、インタラクション、およびリソースが自動的に登録されます。
const configuration = {
trackResources: true,
trackErrors: true,
trackInteractions: true
};
const RNApp = props => {
useEffect(() => {
/**
* ここではダミー値を使用します。
* 目的は RUM イベントのバッファを空にすることです。
*/
DatadogProvider.initialize({
clientToken: 'fake_value',
env: 'fake_value',
applicationId: 'fake_value'
});
}, []);
const navigationRef = useRef(null);
return (
<DatadogProvider configuration={configuration}>
{/* アプリのコンテンツをここに配置 */}
</DatadogProvider>
);
};
AppRegistry.registerComponent('RNApp', () => RNApp);
Android で重複する React Native インタラクションを除去するには、EventMapper を使用してネイティブ側で React Native インタラクションをフィルタリングします。
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
}
}
// RUM 設定で使用します
rumConfiguration.setActionEventMapper(RNActionEventMapper())
ProGuard の難読化を有効にしている場合は、リリースビルドでターゲットパッケージが難読化されないようにルールを追加してください。
制限
React Native 構成で resourceEventMapper
や actionEventMapper
を指定している場合、マッパーで null
を返すとリソースやアクションはドロップされません。
この機能を維持するには、以下のスニペットをプラットフォームごとのネイティブ設定に追加してください。
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
}
// ここでカスタムのイベントマッパーロジックを追加可能
return event
}
})
.setActionEventMapper(object : EventMapper<ActionEvent> {
override fun map(event: ActionEvent): ActionEvent? {
if (event.context?.additionalProperties?.containsKey("_dd.action.drop_action") == true) {
return null
}
// ここでカスタムのイベントマッパーロジックを追加可能
return event
}
})
RUM.Configuration(
applicationID: applicationId,
resourceEventMapper: { resourceEvent in
if resourceEvent.context?.contextInfo["_dd.resource.drop_resource"] != nil {
return nil
}
// ここでカスタムのイベントマッパーロジックを追加可能
return resourceEvent
},
actionEventMapper: { actionEvent in
if actionEvent.context?.contextInfo["_dd.resource.drop_action"] != nil {
return nil
}
// ここでカスタムのイベントマッパーロジックを追加可能
return resourceEvent
}
)
その他の参考資料