クソ雑魚エンジニアのメモ帳

学んだことを書くところ

Vue.jsのエラーハンドリングについて調べた件(後編)

おはようございます。GW1日目ですね。

前回の続きで、今回は「エラーの情報をどこに集約する?」という点について書いていきたいと思います。

  1. どうやって検知する?
  2. どこにエラーの情報を集約する? ←イマココ

検証に使用したコードはこちら デモはこちら スライドはこちら

概要

集約する場所として、自作のログ収集サーバーを挙げられる方もいらっしゃるとは思います。ですが、それなりにコストがかかり、なおかつそこそこの可用性も必要なはずだと思うので、わざわざ自前で作る意味・利点がほとんどないと思います。

そこで、集約する場所として、世間一般的にどのようなサービスを使うのがポピュラーなのか調べてみた結果、何個かありました。

  • googleAnalytics
  • Sentry
  • bugsnag
  • Rollbar

順に紹介していきます。

googleAnalytics

言わずと知れた、googleアクセス解析ツールです。世の中のほとんどのpublicなアプリケーションはgoogleAnalyticsをhtmlに読み込ませているはずです。

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-X"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-XXXXXXX-X');
</script>

これのおまけ機能として、javascriptga()という関数が利用可能になっています。これはページ遷移・イベント発生など様々な情報をトラッキングするための関数です。そのトラッキングできルものの一つに例外があります。

developers.google.com

使い方は、単にエラーが発生した際に以下のフォーマットで関数を呼び出すだけ。

ga('send', 'exception', {
    'exDescription': {記録したいエラー情報(string)},
    'exFatal': false
  })

実際に呼び出した後は、googleAnalyticsのダッシュボードから簡単に確認でき、OSバージョンだったり画面の解像度だったり豊富な情報を組み合わせてグラフを作成できます。

f:id:Kouchannel55:20190810150900p:plain

ただし、デフォルトの状態では確認できません。メニューの カスタム>カスタムレポート>新しいカスタムレポート からレポートを作成する必要があります。

f:id:Kouchannel55:20190810151106p:plain

前編にて紹介した Vue.config.errorHandler と合わせて使用するとこんな感じ

Vue.config.errorHandler = function(err, vm, info) {
  // eslint-disable-next-line no-undef
  ga('send', 'exception', {
    'exDescription': 'catched by `Vue.config.errorHandler`\n' + err.toString(),
    'exFatal': false
  })
}

利点・欠点を挙げます。まず利点

  • お金が100%かからない
  • すでにga()で他のトラッキングをしている場合、簡単に導入できる
  • gaのダッシュボードを利用しておしゃれなグラフを作成できる

欠点

  • 初期の設定がめんどくさい
  • gtag()関数とごちゃまぜになりがち
  • エラーの情報として、単一の文字列しか渡せないかつ最大150バイトまで
    • スタックトレースとかlineNoとかは単一の文字列に含めて、後で自分でパースしなければならない

Sentry

こちらはアクセス解析ツールというよりログ収集ツールです。ログの収集に特化しています

しかも、公式ドキュメントに紹介されているのでとても便利そうな予感がします!!!

Error tracking services Sentry and Bugsnag provide official integrations using this option. https://vuejs.org/v2/api/#errorHandler

さらに、Sentryには、Vuejs専用のライブラリが存在します。

sentry.io

docs.sentry.io

サインアップすると 言語選んで、ライブラリ入れて、その場で発火したかどうかテストできるので本当に開発者に優しいです。

f:id:Kouchannel55:20190810152915p:plain
言語を選んで

f:id:Kouchannel55:20190810152910p:plain
試しに発火させましょう

yarn add @sentry/browser @sentry/integrations
import Vue from 'vue'
import * as Sentry from '@sentry/browser';
import * as Integrations from '@sentry/integrations';

Sentry.init({
  dsn: 'https://<key>@sentry.io/<project>',
  integrations: [new Integrations.Vue({Vue, attachProps: true})],
});

一瞬。しかも収集が簡単なだけでなくダッシュボードはとても充実しています!以下のキャプチャをご覧ください。

f:id:Kouchannel55:20190810153307p:plain
一覧画面

f:id:Kouchannel55:20190810154021p:plain
詳細画面

めちゃくちゃ見やすい。一覧画面はエラーの種類ごとにまとめられてるし、直近の発生回数や発生したユーザー数、さらにはこのエラーを修正するための開発者をアサインする機能までついております。

詳細画面ではIP/OS/ブラウザ/スタックトレースが見えます。これらはgaでも取得できましたが、驚くのは「エラーが発生するまでの直近のユーザーの操作」が記録されていること。こんなんあればエラーの原因もすぐにわかりそうですね。控えめに言って神

しかも無料プランでこの情報量が見れるんです。太っ腹。

sentry.io

ただし、一つ注意しないといけないのは Vue.config.errorHandler と併用できない点です。 Vue.config.errorHandler を定義していると、Sentryが発火しなくなります。

おそらく、Sentryの中で Vue.config.errorHandler を定義していて、そのあとにVue.config.errorHandlerを自前で書くと上書きされてしまうのだと思います。気をつけましょう

利点・欠点を挙げます。まず利点

  • 情報量が多い
  • 情報が見やすい
  • エラーに対する開発者のアサイン・イシュー管理まで可能
  • ドキュメントが丁寧

欠点

  • チームで利用するには課金が必要

bugsnag

こちらもSentry同様、Vuejs公式ドキュメントに紹介されています。Sentryと同じように便利そうです。

docs.bugsnag.com

// ES module-style import
import bugsnag from '@bugsnag/js'
import bugsnagVue from '@bugsnag/plugin-vue'

// commonjs/node-style require
var bugsnag = require('@bugsnag/js')
var bugsnagVue = require('@bugsnag/plugin-vue')

var bugsnagClient = bugsnag('YOUR_API_KEY')

bugsnagClient.use(bugsnagVue, Vue)

Rollbar

rollbar.com

こちらはVuejs公式ドキュメントには紹介されていませんが、Vuejs専用のライブラリがあります。Sentryと同じように便利そうです。

import Vue from 'vue'
import App from './App'
import router from './router'

var Rollbar = require('vue-rollbar');

Vue.config.productionTip = false;

Vue.use(Rollbar, {
     accessToken: 'ACCESS-TOKEN',
     captureUncaught: true,
     captureUnhandledRejections: true,
     enabled: true,
     source_map_enabled: true,
     environment: 'production',
     payload: {
       client: {
            javascript: {
               code_version: '1.0'
            }
       }
     }
});

new Vue({
 el: '#app',
 router,
 render: h => h(App)
})

まとめ

  • 基本的にはSentryが神
  • すでにgoogleAnalyticsのレポートをゴリゴリに使っているPJの場合には、エラーの収集もgoogleAnalyticsを使用すると他の指標といい感じのグラフが作れそう