Sentry に Vue + Nuxt.js のエラーをユーザ情報と紐付けて送信するまで

f:id:branu_techblog:20210929162111p:plain


こんにちは、開発部の山﨑です!

突然ですが、皆さんはご自身で開発に携わった Web アプリケーションをユーザが利用した時、どのくらい、どのようなエラーが起きているのか気になった事はありますか?

もちろんエラーが起きないに越した事はありませんが、私個人の感想としては、Branu に入社するより以前は、かなり際どい作業を行った際はリリース直後数日くらい緊張の中で仕事をする事もありました。(会社の電話が鳴るたび胸がドキドキ)

不具合がすぐに発覚するならダメージが最小限に抑えられた分まだいくらか儲けものですが、

一番よくないのは、

「不具合が発生し続けているのにも関わらずそれに気が付く事ができない事」

です。つまり不具合の解決は、

「不具合があると気が付けるようにする仕組み作り」

から始まると言っても過言ではありません。

そして、そのように「気が付くことができなかった」事によって放置された問題は、得てしていざ気がついた時には既に手遅れになっている事が多いものです。

そこで Branu では、本番環境で起きたエラーを検知、記録してくれるサービス「Sentry」を利用していますので、ご紹介します。

https://sentry.io/welcome/

Sentry は、いつ、どの環境で、どんなエラーが起きたか、発生頻度は、回数は、発生当時のリリースバージョンはいくつか、などなど、あらゆる情報を検知、送信、保管してくれる、 Web エンジニアみんなの味方です。

Sentry に送信されるエラーが全てを解決してくれるわけではありませんが、少なくともエラーを監視できている事は、ユーザの皆さんのためにも、開発者の安心のためにもなります。

この Sentry ですが、私が入社した頃には既に導入済みのサービスであったので、今回はサンプルを作成しながら導入してみて、実際にデータが送信される所までを追っていこうと思います。

サンプルアプリケーションの作成


まずは何はともあれエラーを検知するためのアプリケーションを作らねばなりません。

Nuxt.js のアプリケーションの作成方法は公式ドキュメントが詳しいので、そちらをご覧ください。

https://ja.nuxtjs.org/docs/2.x/get-started/installation

インストール時 UI のフレームワークを選択しますが、今回は Element UI を選択しました。

Element UI については以下をご覧ください。

https://element.eleme.io/

また、この記事では nuxt-property-decorator を利用し、アノテーションを使用して Component を作成します。

nuxt-property-decorator の導入については以下をご覧ください。

https://www.npmjs.com/package/nuxt-property-decorator

さて、導入が完了したら次は動作を検証するために簡単な UI を作成します。

今回は画像のようにエラーが発生しない通常のボタンと、エラーが発生し Sentry にエラーログを送信するボタンの2種類作成しました。とっても露骨で分かりやすいですね!

f:id:branu_techblog:20210929160738p:plain

サンプルコードを以下に記載します。

また、今回はアプリケーション作成時に自動生成される index.vue を書き換える事にします。

<template>
  <div>
    <ElCard>
      <ElButton @click="onClickNormalButton" type="primary">エラーが起きないボタン</ElButton>
      <ElButton @click="onClickErrorButton" type="danger">エラーが起きるボタン</ElButton>
    </ElCard>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import axios from 'axios'
import { Component } from 'nuxt-property-decorator'

@Component
export default class SentrySampleComponent extends Vue {

  onClickNormalButton() {
    console.log('Normal button clicked')
  }

  onClickErrorButton() {
    console.log('Error button clicked')
    axios.get('<https://google.co.jp/test>')
  }

}
</script>

Axios を import し、リクエストしてもエラーが返却されそうな URL に対して GET リクエストを送信してみます。

本来 MVVM アーキテクチャの観点から Component に Axios を import する事はあまり望ましくありませんが、ここではサンプルにつき簡単のためこのようにしています。

結果は以下の通りになりました。しっかりエラーが発生している事が確認できますね。

f:id:branu_techblog:20210929160849p:plain

 

Nuxt.js アプリケーションに Sentry を導入


それでは、発生したこのエラーを Sentry に送信し、エラーの発生を検知できるようにしてみましょう。早速 Sentry に登録してみます。

冒頭のリンクから Sentry のサイトにアクセスすると登録が可能です。

「TRY SENTRY FOR FREE」ボタンから登録を行いましょう。

f:id:branu_techblog:20210929160911p:plain

メールアドレスの認証を終えると、使用する言語、フレームワークを選択する事ができます。

f:id:branu_techblog:20210929160932p:plain

モダンなフロントエンド、バックエンドの言語、フレームワークは大体網羅されている事がわかりますね。

今回は「VUE」を選択し、「Create Project」をクリックします。

次のページで、Vue.js への Sentry の導入方法が記載されていますが、今回は Nuxt.js への導入ですのでこちらのコードはあまり参考にはなりません。

必要なのはコード中に記載されている DSN のみとなりますので、こちらをコピーしておきます。

次に、Sentry SDK を導入します。

アプリケーションの配置されているディレクトリで、以下のコマンドを実行します。

$ yarn add @nuxtjs/sentry

package.json をチェックして導入が確認できたら、nuxt.config.js に Sentry SDK を利用するよう設定を追加します。

まずは modules に先程追加したパッケージを追加し、続いて DSN を設定します。

// Modules: <https://go.nuxtjs.dev/config-modules>
  modules: [
    "@nuxtjs/sentry"
  ],

  sentry: {
    dsn: "https://****************************************.ingest.sentry.io/********",
  },

これでひとまず設定は完了です。

それでは早速エラーを発生させて Sentry を確認してみましょう!

私の体感ですが、エラーが発生してから Sentry の画面に表示されるまで、およそ1、2分かかるようです。すぐにエラーが表示されなくても気長に待ちましょう。

f:id:branu_techblog:20210929161003p:plain

 

結果、しっかり誤ったリクエストがエラーログとして記録されました。

便利なサービスなのに案外導入はお手軽ですね。

ログ送信時にユーザ情報を付与する


さて、ここまででエラーをログとして記録する事ができましたが、

本番環境とはいえ、動作の確認のために開発者が用意したアカウントというのは当然あるものです。

そこで、一般のユーザと開発者とを見分けるために、ユーザ情報を付与しつつ Sentry にログを送信する場合を考えます。

まずは、Nuxt.js の初期化時に呼ばれる plugin を作成します。

今回はざっくり以下のように作成しました。実際に利用する際は Store からユーザデータを取得する必要があるかと思います。

export default ({ app }) => {
  app.$sentry.configureScope(scope => {
    scope.setUser({
      id: '123',
      username: 'Branu 太郎',
      email: 'sample@example.com'
    })
  })
}

さらにこれが Nuxt.js 起動時に読み込まれるよう、nuxt.config.js の plugins プロパティに配置します。

ここで記載された plugin は、Nuxt.js の初期化時に上から順番に実行されますので、plugin 追加時には実行順序を十分に配慮する必要がありそうです。

今回はプロジェクト作成時に追加された Element UI のプラグインが存在するのみですので、気にせずその次の要素に記載を追加します。

// Plugins to run before rendering page: <https://go.nuxtjs.dev/config-plugins>
  plugins: [
    '@/plugins/element-ui',
    '~/plugins/sentry-add-user-info'
  ],

準備を行った状態で、アプリケーションを再起動し、赤色のボタンをクリックしてみます。

結果を確認した所、以下のようになりました。

f:id:branu_techblog:20210929161019p:plain

 

先程設定した Email、 ID、 ユーザ名が Sentry に記録されていますね。

これで社内向けアカウントと一般ユーザを見分ける事ができ、また、特定のユーザにおいて発生した現象についてもエラーの追跡が可能になりました。

まとめ


本来はここから更に、本番環境で動作している minify & uglify されたコードのソースマップをビルド時に Sentry に送信する事によって、可読性の高いコードで確認作業を行う事が可能です。

時間の都合上このあたりまでしか記載ができませんでしたが、フロントエンドの不具合は、動作確認時は問題なかったにも関わらず、特定の条件を満たしてしまったユーザから「何故か動かないのですが...」と問い合わせをいただく事もあります。

そういった場合、開発者サイドではどうしても原因を知るために状況を再現したいものなのですが、中々シビアな条件下でのみ発生する不具合であったりすると、再現だけでも相当な労力が掛かるものです。

Sentry を使えば、特定のユーザによって発生したエラーを追うことができますので、開発者としてはかなり助かるサービスなのではないでしょうか!

それでは、最後までご覧いただきありがとうございました!