ExpoアプリにSentryを導入して検証してみる


エラー監視サービスのSentryをExpoを使用して開発しているReact Nativeアプリに導入し、ある程度動作検証してみます。

Sentry

画面に沿ってアカウント作成後、

最初はこのようにチュートリアルのような形でプロジェクトを作成できますが、ダッシュボード(トップページ)のProjectsCreate Projectからプロジェクト名、プラットフォーム(React-Native)を指定して作成します。

ここからはExpoのドキュメントに従って進めます。
https://docs.expo.io/versions/latest/guides/using-sentry/

必要な情報は

  • DSN
    プロジェクト作成時に案内が出ているかもしれませんが、SettingsProjects→プロジェクトを選択→Client Keys(DSN)からも取得できます。
  • 組織名(Organization)
    個人で登録した際は個人名を指定してるかと思います。ケバブケース化されています。
  • プロジェクト名
    ケバブケース化されています。
  • Authトークン
    ダッシュボードのSettings→パンくずリストのSettingsで設定のトップへ→API KeysAuth Tokensページで作成します。

Authトークンの作成時には、Expoのドキュメントに従ってproject:writeをチェックしておきます。

Expoプロジェクト側で設定・初期化する部分は、app.jsonと最初に呼ばれるJSファイルのみです。
まずはsentry-expoをインストールします。publishごとに生成されるIDを取得するため、expo-constantsもインストールしておきます。

$ npm install sentry-expo expo-constants

app.jsonexpo.hooks.postPublishにこのように設定を追加します。
publish時にSentryにソースマップファイルをアップロードしているようです。

app.json
{
  "expo": {
    "hooks": {
      "postPublish": [
        {
          "file": "sentry-expo/upload-sourcemaps",
          "config": {
            "organization": "組織名",
            "project": "プロジェクト名",
            "authToken": "Authトークン"
          }
        }
      ]
    }
  }
}

JS側でSentrySDKをセットアップします。

App.js
import * as Sentry from "sentry-expo";
import Constants from 'expo-constants';

Sentry.setRelease(Constants.manifest.revisionId || "DEV");
Sentry.init({
  dsn: 'DSNをここに指定',
  enableInExpoDevelopment: true,
  debug: __DEV__
});

initメソッドにオプションを指定して初期化します。
一応デバッグモードは開発時のみtrueになるようにしておきますが、それほど気にしなくていいみたいです。
先ほどインストールしたexpo-constantsからリビジョンIDをSentryにセットしてあげると、どのバージョンでエラーが発生したかが特定できます。

とりあえずcaptureExceptionメソッドを使って、Expo Clientの開発環境でエラーを発生させてみます。
setUserメソッドでユーザーを特定できるようにしたりできるようなので、これも使ってみます。

App.js
Sentry.setUser({"email": "[email protected]"});
Sentry.captureException(new Error('error!'));

Expo画面にはエラーが表示されました。

The stack is nullとあります。Sentryがうまくスタックトレースにアクセスできてないような感じです(?)。

しかしSentryのissuesを見ると、ちゃんとエラーイベントが取得できているようです。ユーザーも問題なくセットできています。

今度は明示的にcaptureExceptionを使うのではなくエラーを発生させてみます。

App.js
Sentry.setUser({"email": "[email protected]"});
global.foo(); // 存在しないfunction

こちらは当然Expo Clientの画面では通常のエラー表示になります。

Sentryの方でも問題なくエラーが取得できていました。

ちなみに、先ほどsetReleaseDEVが入るようにしているので、RELEASE欄にはちゃんとDEVと表示されています。

では、リリース後のアプリでも確認してみます。
以前作ったQRコード生成のアプリにこっそりとエラーを発生させる仕組みを仕込んで、Expoでpublishしてみます。

App.js
<TouchableWithoutFeedback
  onPress={() => {
    throw new Error('error test!');
  }}
>
  <View
    style={{
      position: "absolute",
      top: 0,
      left: 0,
      width: 70,
      height: 70
    }}
  />
</TouchableWithoutFeedback>

タップするとエラーを発生させる隠しボタンを追加してpublishでOTAアップデートし、
アプリを起動してエラーを発生してみます。

Sentryの方でも問題なくエラーが取得できています。

[追記]
Androidでも確認しました。

setReleaseで設定しているので、RELEASE欄にはExpoが自動で生成しているユニークな値が入っています。

エラーの詳細を見ると、元ソースもエラーが発生した位置もちゃんと閲覧できます。
ユーザーのいないしょぼいアプリとはいえ、そのままにしていたら普通にまずいのですぐ消しました。

今回試してみたのはRELEASEUSERのみですが、他にも色々とコンテキストを設定できるようです。
詳しくはドキュメントを参照してください。
ただし、一部React Nativeでは対応していないものがあるようです。例えばコンソールの内容を送信するためのSentry.Integrations.CaptureConsoleなどは使用できませんでした。