[ReactNative]クライアントのデバイス情報をFirebaseログに出力したい


## 実現したいこと

- モバイルアプリを、ReactNative、FirebaseSDKを使って実装している
- クライアントサイドのデバイス情報、OS共通設定(例.位置情報の許可状態など)をFirebaseログに出力したい

## なぜ？
本番での問い合わせ対応や障害解析時に、事象発生時のクライアントサイドの設定がどのようなものだったかを、ユーザーに事細かにヒアリングすることはできません。
事象発生時の状態を正確に把握できるように、イベントログに出力します。

## 参考にしたサイト

https://ws-4020.github.io/mobile-app-crib-notes/react-native/santoku/decisions/adr-006-logging/

https://firebase.google.com/docs/analytics/events?hl=ja&platform=web

https://firebase.google.com/products/analytics?hl=ja#:~:text=Firebase%20%E5%90%91%E3%81%91%20Google%20%E3%82%A2%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AF%E3%82%B9%E3%81%A7%E3%81%AF,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82

https://dev.classmethod.jp/articles/firebase-analytics-event/

https://github.com/react-native-device-info/react-native-device-info


## 実装の方針

FirebaseSDKを使用している場合、主要なユーザープロパティが自動で取得されます。
これをベースに他のOS設定情報を`react-native-permissions`ライブラリを使用して適宜取得していきます。

Firebase SDKで自動的に収集されるユーザープロパティは以下のものがあります。
これらのプロパティは、Firebase Analytics のユーザープロパティとして利用できます。ただし、Firebase Analytics のポリシーに従って、プライバシーに注意する必要があります。

 
- App Version: アプリのバージョン番号
- OS Version: オペレーティングシステムのバージョン番号
- Device Model: デバイスのモデル名
- Platform Version: Firebase SDK のバージョン番号
- Previous App Version: アプリの前回起動時のバージョン番号
- User ID: Firebase Authentication を使用してアプリにログインしたユーザーの ID
- Language: デバイスで設定された言語
- Timezone: デバイスのタイムゾーン
- Campaign: アプリのインストールを促進するために使用されたキャンペーン名
- Device Brand: デバイスのブランド名
- First Open Time: アプリの初回起動時間
- Last Deep Link Referrer: 直前の Deep Link によるアプリの起動元
- User Properties: Firebase Analytics SDK で設定されたカスタムユーザープロパティ

## コードサンプル

Firebaseの`DeviceInfo`ライブラリを使用して、各種ユーザプロパティを取得します。
また、`firebase.auth().currentUser` を使用して、Firebase Authentication を使用してアプリにログインしたユーザーのIDを取得しています。
位置情報の許可状態は`react-native-permissions`ライブラリを使用して取得しています。

最後に`firebase.analytics()` を使用して、ログに出力しています。

```javascript

import firebase from 'firebase/app';
import 'firebase/analytics';
import 'firebase/auth';
import { Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info';
import { check, PERMISSIONS, RESULTS } from 'react-native-permissions';

// Firebaseの初期化
if (!firebase.apps.length) {
  firebase.initializeApp(firebaseConfig);
}

// デバイス情報を取得し、Firebaseログに出力する
async function logDeviceInfo() {
  const user = firebase.auth().currentUser;
  const appVersion = DeviceInfo.getVersion();
  const deviceModel = DeviceInfo.getModel();
  const osVersion = DeviceInfo.getSystemVersion();
  const timezone = DeviceInfo.getTimezone();
  const language = DeviceInfo.getDeviceLocale();
  let locationMode = '';
  let accessFineLocationPermission = '';
  let accessCoarsePermission = '';
  let accessBackgroundLocationPermission = '';
  let powerSaveMode = '';

  if (Platform.OS === 'ios') {
    // iOSの場合、位置情報の許可状態をチェックする
    const permission = await check(PERMISSIONS.IOS.LOCATION_WHEN_IN_USE);
    locationMode = permission === RESULTS.GRANTED ? 'granted' : 'denied';
  } else {
    // Androidの場合、位置情報の許可状態とデバイス情報を取得する
    const permission = await check(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION);
    accessFineLocationPermission = permission === RESULTS.GRANTED ? 'granted' : 'denied';
    accessCoarsePermission = await check(PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION);
    accessCoarsePermission = permission === RESULTS.GRANTED ? 'granted' : 'denied';
    accessBackgroundLocationPermission = await check(PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION);
    accessBackgroundLocationPermission =
      permission === RESULTS.GRANTED ? 'granted' : 'denied';
    powerSaveMode = await DeviceInfo.isBatterySaverEnabled();
  }

  // Firebaseログにデバイス情報を出力する
  firebase.analytics().logEvent('device_info', {
    app_version: appVersion,
    device_model: deviceModel,
    os_version: osVersion,
    user_id: user ? user.uid : '',
    language: language,
    timezone: timezone,
    location_mode: locationMode,
    access_fine_location_permission: accessFineLocationPermission,
    access_coarse_permission: accessCoarsePermission,
    access_background_location_permission: accessBackgroundLocationPermission,
    power_save_mode: powerSaveMode,
  });
}

```

### 補足

Firebaseのログ分析のベストプラクティスとして、属性名にスペースを含まないことが推奨されています。そのため、 `access_fine_location_permission` や`access_coarse_permission` などの属性名にスペースを含めず、スネークケースに変更しました。また、デバイス情報をオブジェクトでまとめてログに出力するのではなく、 `firebase.analytics().logEvent()` の第二引数に直接プロパティを指定することで、ログの可読性を向上させています。


## まとめ

ReactNative、FirebaseSDKを使って、クライアントサイドのデバイス情報、OS共通設定(例.位置情報の許可状態など)をFirebaseログ出力する方法をまとめました。
問い合わせ対応やアプリの利用分析の観点で必要な項目を洗い出し、適宜追加していってください。