ReactアプリケーションにSentryを導入してみる(Webpack使用)


React Native版の記事はこちらです。

SentryをReactアプリケーションに導入してみます。前半はReactに限らず、ブラウザ上で動かすJSに共通する設定になります。

設定に必要な基本的な情報はすでに取得している状態を想定して進めていきます。
最低限の使用の場合は、@sentry/browserをインストールし、以下のコードをJSの最上位ファイルに追加するだけです。

$ npm install @sentry/browser
App.js
import * as Sentry from '@sentry/browser';

Sentry.init({
  dsn: "ここにプロジェクトのDSN"
});

throw new Error('error'); // 適当にエラーをスローしてみる

ローカルで動かしてみると、問題なくSentryのissuesの中にエラーイベントが入りました。

このままでも使えるのですが、ソースマップファイルやリリース情報をSentryに送信してログをより見やすくするために、Webpackの設定を追加してみます。

公式のドキュメントに従いますが、必要のない設定項目は割愛しています。
まずは.sentryclircというファイルを追加して、Webpackでビルドする時にSentryのプロジェクトにアクセスできるようにします。

.sentryclirc
[auth]
token=Authトークン
[defaults]
org=組織名
project=プロジェクト名

公式のWebpackプラグインをインストールして、

$ npm install --save-dev @sentry/webpack-plugin
webpack.config.js
const SentryWebpackPlugin = require('@sentry/webpack-plugin');

...
plugins: [
  new SentryWebpackPlugin({
    include: "build/static/js"
  }),
  ...
]
...

Webpackのpluginsに追加します。ここでincludeに、ソースマップファイルを送信してほしいJSのディレクトリを指定します。
デフォルトではディレクトリ内のnode_modules以外の全てのJS/CSSファイルが送信されます。

そのほかのオプションはこちら
オプション一覧の最初の行を確認してください。このオプションにrelease文字列を渡せば任意のリリース情報を設定できますが、デフォルトではgitのコミットIDがそのままリリース情報として使用されるようです。

この段階でもしgitの設定をしていなければ、git initからaddcommitまで一通りしてから、Webpackでビルドしてみます。

Creating an optimized production build...
> Analyzing 6 sources
> Rewriting sources
> Adding source map references
> Bundled 6 files for upload
> Uploaded release files to Sentry
> File upload complete

Source Map Upload Report
  Minified Scripts
    ~/2.0f707d42.chunk.js (sourcemap at 2.0f707d42.chunk.js.map)
    ~/main.663c8045.chunk.js (sourcemap at main.663c8045.chunk.js.map)
    ~/runtime-main.d5ffe654.js (sourcemap at runtime-main.d5ffe654.js.map)
  Source Maps
    ~/2.0f707d42.chunk.js.map
    ~/main.663c8045.chunk.js.map
    ~/runtime-main.d5ffe654.js.map
Compiled with warnings.

こんな感じでコンソールにソースマップの情報が表示されます。
あとは何かしらサーバーにデプロイしてみてください。これでSentryの準備は完了です。

デプロイしたページを開いて、エラーを発生させてみると、

まず、RELEASE欄にちゃんとコミットIDが入っているのがわかります。

ソースマップが送信できているので、エラーの発生箇所も確認できます。

ReactのError Boundaryでレポートダイアログを使用してみる

Sentryには、エラーが発生した時にユーザーからフィードバックを得るためのフォームを表示する機能が用意されています。

User Feedback
https://docs.sentry.io/enriching-error-data/user-feedback/?platform=javascript

これをReactのError Boundaryで使用する例がドキュメントに載っていたので、試してみました。
https://docs.sentry.io/platforms/javascript/react/#error-boundaries

SentryBoundary.js
import React, { Component } from 'react';
import * as Sentry from '@sentry/browser';

class SentryBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { eventId: null };
  }

  // エラーが発生した時にstateを更新する
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  // エラーが発生した時にSentryのscope(エラーの情報をまとめるもの)を作成
  componentDidCatch(error, errorInfo) {
    Sentry.withScope((scope) => {
      scope.setExtras(errorInfo); // エラーの情報をセット
      const eventId = Sentry.captureException(error); // Sentryにエラーを送信し、IDを取得
      this.setState({ eventId }); // エラーIDをstateにセット
    });
  }

  render() {
    if (this.state.hasError) { // エラーが発生したら画面にボタンを表示する
      return (
        <button
          onClick={() => Sentry.showReportDialog({ eventId: this.state.eventId })} // Sentryのダイアログを表示
        >
          エラーを報告する
        </button>
      );
    }

    // 何もない場合はそのまま子要素を表示
    return this.props.children;
  }
}

export default SentryBoundary

Error Boundary自体はReactの機能(実装パターン)です。

App.js
<SentryBoundary>
  <ChildComponent />
</SentryBoundary>

使用する単位は任意ですが、このように子要素をError Boundaryで囲っておいて、子要素のレンダリングで何かしらエラーが発生した時に代りに別の要素を表示したりすることができます。

この例では、エラーが発生した時に「エラーを報告する」ボタンを表示し、ボタンが押されたらSentryのダイアログを表示するようにしています。

実際にError Boundaryの子要素でエラーが発生するようにしてみます。

例えば下記のように、Reactのレンダリングとは関係ないところでエラーを発生させてもError Boundaryはcatchしません。

ChildComponent.js
import React from 'react';

function ChildComponent() {
  window.setTimeout(() => {
    throw new Error('error test');
  }, 1000);
  return (
    <div>
      エラーを発生させます...
    </div>
  );
}

export default ChildComponent;

このように、Reactのレンダリングが不可能になるエラーが発生した場合にError Boundaryがエラーをcatchします。
(例えば、returnするJSXの中で存在しないfunctionを叩く)

ChildComponent.js
import React from 'react';

function ChildComponent() {
  return (
    <div>
      エラーを発生させます...
      {window.foo()}
    </div>
  );
}

export default ChildComponent;


起動してみると、「エラーを発生させます...」という文言の代わりにボタンが表示されました。

これを押すと、このようなSentryのダイアログが表示されます。

名前とメールアドレス、詳細を入力して送信してみると...

Sentryのエラー詳細の方にUser Feedbackがちゃんと追加されました。

ダイアログの文言なども変更できるようです。
https://docs.sentry.io/enriching-error-data/user-feedback/?platform=javascript#customizing-the-widget

言語は自動的にブラウザの言語から設定されるようですが、一番大きなタイトルが英語のままだったので、オプションを渡してみます。ユーザーの名前・メールアドレスは最初から指定すれば最初から入力しておけるようです。

SentryBoundary.js
...
        <button
          onClick={() => Sentry.showReportDialog({
            eventId: this.state.eventId,
            title: "すみません、問題が発生しました",
            user: {
              email: "[email protected]",
              name: "React 和子"
            }
          })}
        >
          エラーを報告する
        </button>
...

問題なく設定できました。

ダイアログにはclassがセットされているので、見た目のカスタマイズもCSSで簡単にできそうです。

以上、ReactNativeとWebでSentryを触ってみました。特に嵌るところもなく、スムーズに導入できました。