見えないエラーを見れるようにする & 効率化のため Rollbar を導入した話

こんにちは。エンジニアの鈴木(yamotuki)です。 今回は本番や開発環境でエラーが起こったときに、効率よく対応ができるように Rollbar というツールを導入した話です。

Rollbar とは

公式はこちら

JavaScriptPHPで発生したエラー詳細をSlack通知してくれます。
通知や画面詳細ページは後述してあります。

導入目的

導入目的としては、フロントエンドのエラー検知とバックエンドサーバのエラー対応の簡素化の二つがあります。

1. フロントエンドのエラー検知

Universalモード(SSRCSRでシームレスに動作する仕組み)で動いているNuxt.jsによるアプリケーションのエラー検知の仕組みが欲しい、というのが一番最初の導入のきっかけでした。 SSRで動いている部分については、なんとかしてログ出力をしてエラーを検知することをできるかもしれません。しかし、それぞれのクライアントで動いているJavaScriptについてはコンソールログにだけ出ている状態で、エラーが起こっていることを知ることができない、というのが一般的なお困りごとかと思います。
Rollbarを使用すれば、エラー発生時のSSR/CSRを問わない統一的な仕組みとしてエラー通知を行うことができます。

2. バックエンドサーバのエラー対応の簡素化

弊社では AWS CloudWatch によりCloudFrontやEC2で発生した5xx系のアラートを検知してSlack通知していますが、以下のように情報量が少ないのが悩みの種でした。問題調査をするためにAWSコンソールを開いて、該当のアクセスログや、対応するエラーログを確認する必要があり、ちょっとしたエラー確認でも手間がかかっていました。

f:id:yamotuki:20210615170011p:plain
調査に時間がかかるアクセスログの5xx検知

導入結果

フロントエンドのエラーを検知して対処できた

振り返って見てみると、この記事にかけるような面白いエラーはログ保存期間の間には起こっていませんでした。 導入当初には広範囲のユーザ影響が出てしまう致命的なエラーが何度か起こっていましたが、逐次対応してきた結果、最近ではエラー頻度はぐっと減っているようです。

バックエンドサーバのエラー対応が効率化された

Rollbar を使うと以下のように何が起こったのか詳細がSlack通知され、さらに詳細を見たければリンクをクリックするだけでいいので劇的に調査が楽になります。

f:id:yamotuki:20210615165935p:plain
RollbarからSlack通知

詳細画面に行くと以下のような情報を見ることができます

  • 何回、いつ、影響したユーザ(IP)数
    • 障害対応においては、どれくらい深刻な障害なのか?というのは重要な情報なので、これがログをいちいち漁って見なくてもまとまっているのが良い
  • スタックトレースを引数つきで見れる
    • 「最終的にエラーが起こっている場所は分かったけど、途中でなんでこうなった?」みたいなケースも簡単に追跡することができます

f:id:yamotuki:20210617190317p:plain
詳細ビュー

導入でハマった点&工夫した点

Nuxt.js

nuxt-rollbar-module を用いています。細かい導入方法についてはドキュメントを参照してください。
導入に際して工夫した点だけ共有します。

  • 問題点
    • local で rollbar 通知をすると回数制限を無駄にしてしまう。TOKENを入れないとrollbar通知するためのコードが無視されるので、localでうまく動いているか判断できない。
  • 解決策
    • plugins に dummy-rollbar.ts を作成し、rollbar を呼んでいるところは local では console.log にマッピングするようにしました。
import { Context } from '@nuxt/types'

export default (ctx: Context, inject: (key: string, value: any) => void) => {
  const rollbar = ctx.$rollbar ?? {
    debug: (...e: any) => console.log(...e),
    info: (...e: any) => console.log(...e),
    warn: (...e: any) => console.log(...e),
    warning: (...e: any) => console.log(...e),
    error: (...e: any) => console.error(...e),
    critical: (...e: any) => console.error(...e)
  }
  // ref: https://github.com/gaelreyrol/nuxt-rollbar-module/blob/develop/lib/templates/rollbar-client.js#L46-L47
  ctx.$rollbar = rollbar
  inject('rollbar', rollbar)
}

plugins の差し込みについては公式ドキュメントを参考にしてください。

Laravel

基本はドキュメントを読んで導入かと思いますが、ハマったところだけ共有しておきます。

config/logging.php で以下のような設定を入れて、error log と rollbar の両方に通知するようにしてあります。

<?php

    'channels' => [
        'errorlog_and_rollbar' => [
            'driver' => 'stack',
            'channels' => ['errorlog', 'rollbar'],
            'ignore_exceptions' => true,
        ],
        'errorlog' => [
            'driver' => 'errorlog',
            'level' => 'debug',
        ],
        'rollbar' => [
            'driver' => 'monolog',
            'handler' => \App\Providers\Rollbar\MonologHandler::class,
            'access_token' => env('ROLLBAR_TOKEN'),
            'level' => 'error',
            // rollbarでマスクしたいパラメータを指定
            'scrubFields' => [
                // user
                'password',
            // 以下略

rollbar の handler として指定している MonologHandler クラスの雛形はこちらです。

雛形の namespace が間違っている問題

こちらのライブラリへのPRが出されているように、namespaceが間違っていていました。

ignore_exceptions の問題

雛形のコードを少し書き換えて使っていたのですが、とある変数の初期化がされておらず無言エラーになり、本番通知がうまくされていませんでした。”無言”である理由はignore_exceptionsがtrueになっているからでした。この設定の意図としては、片方の channel でエラーが起こっても、握りつぶしてもう片方の channel には通知が行って欲しいという意図かと思われます。localで一時的に試しにfalseにしたところ、すぐにどこでExceptionを吐いているか特定し、修正できました。

開発環境にも入れておく

上記の通り、本番環境で迅速にエラー対応するために効果的なのですが、開発環境にも入れておくことで、エラーをユーザに見せる前に修正できることがあります。
開発環境に入れるときに通知先Slackチャンネルの分離に一工夫があったので共有します。

  • 問題点
    • Project ごとで通知先が一箇所しか選べない。Projectはトップレベルの階層で、その下にEnvironmentがあります。Projectの中の Rollbar の Items(エラーの一つ一つ)には Environment が紐づいているので、それによって通知先を分離したいと思っていましたが、できませんでした
  • 解決策
    • Project を開発環境、本番環境で分離しました。Environment が情報として冗長になってしまいますが、Slack通知先を分離することができました。

終わりに

www.wantedly.com

フロントエンドのエンジニアに届かないエラーを可視化することで大きな問題でもすぐに対応できるようになりました。エラー対応を効率化すると、神経をすり減らす障害対応を少し楽にすることができました。

弊社では生産性向上をして、ユーザにより多くの価値を一緒に届ける気持ちを持ったエンジニアを募集しています。