Edited at

Sentry を Laravel と Vue.js に導入してみた

Sentry を導入したので導入手順をまとめました。

PHP (Laravel) と Vue.js の場合について記載しています。


Sentry?

https://sentry.io/

Web アプリケーションで起きたエラー等をロギングできるサービスです。

Slack やメールで通知を送れます。


導入


Laravel 5.x (PHP)

Laravel 用のパッケージがあるのでこれを利用します。記事作成時点での最新バージョンは 0.10.0 です。

getsentry/sentry-laravel: Laravel integration for Sentry

導入方法: Docs - Laravel


基本

パッケージをインストールします。

composer require sentry/sentry-laravel

Laravel 5.4 以下 の場合は config/app.php に設定を追加する必要があるようです。


config/app.php

'providers' => array(

// ...
Sentry\SentryLaravel\SentryLaravelServiceProvider::class,
)

'aliases' => array(
// ...
'Sentry' => Sentry\SentryLaravel\SentryFacade::class,
)


Sentry へのロギングを実施させる仕組みとして、 app/Exceptions/Handler.phpreport() に手を加えます。


app/Exceptions/Handler.php

public function report(Exception $exception)

{
// ここから
if (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->captureException($exception);
}
// ここまで

parent::report($exception);
}


config/sentry.php を生成するため以下のコマンドを実行します。

php artisan vendor:publish --provider="Sentry\SentryLaravel\SentryLaravelServiceProvider"

Sentry の DSN を設定します。

config/sentry.php にべた書きするなり .env に書き込むなりしましょう。


config/sentry.php

return array(

'dsn' => env('SENTRY_LARAVEL_DSN', 'https://foobar@sentry.io/1234567890'),
...

仕上げに Laravel の 500 エラーページを用意します。

サーバエラーが起きた際に、ロギングだけでなくユーザーに詳細な報告を入力してもらうためのフォームを表示できます。


resources/views/errors/500.blade.php


<div class="content">
<div class="title">予期しないエラーが発生しました</div>

@if( app()->bound('sentry') && !empty(Sentry::getLastEventID()) )
<div class="subtitle">Error ID: {{ Sentry::getLastEventID() }}</div>
<script src="https://cdn.ravenjs.com/3.3.0/raven.min.js"></script>
<script>
Raven.showReportDialog({
eventId: '{{ Sentry::getLastEventID() }}',
dsn: '{{ config("sentry.dsn") }}',
user: {
'name': '{{ Auth::User() ? Auth::User()->name : "" }}',
'email': '{{ Auth::User() ? Auth::User()->email : "" }}'
}
})
</script>
@endif
</div>



エラー発生時の認証ユーザー情報をログに含める

Middleware を作成します。

ユーザーの操作中にエラーが発生した時に、どのユーザーでエラーが発生したのかをログと一緒に Sentry に送信できます。


app/Http/Middleware/SentryContext.php

<?php

namespace App\Http\Middleware;

use Closure;

class SentryContext
{
public function handle($request, Closure $next)
{
// ここから
if (app()->bound('sentry')) {
$sentry = app('sentry');

if (auth()->check()) {
$sentry->user_context([
'id' => auth()->user()->id,
'email' => auth()->user()->email
]);
} else {
$sentry->user_context([
'id' => null,
'email' => null
]);
}
}
// ここまで
return $next($request);
}
}


ファイルを作成したら app/Kernel.php$routeMiddleware に登録します。


app/Kernel.php

        ...

'sentry.context' => \App\Http\Middleware\SentryContext::class
];

ロギング時にユーザー情報を含めたいルートに対してこの Middleware を適用します。


routes/web.php

Route::middleware(['auth', 'sentry.context'])->group(function() {

...
}


JavaScript (+Vue.js)

4.0.0 からパッケージが新しくなり、以前の raven-js ではなくなってるっぽい。

同時に導入方法も変わっていたのでこちらもまとめ。

getsentry/sentry-javascript: Official Sentry SDKs for Javascript

いくつかのパッケージがありますが、ブラウザ上で動作する React や Vue 製の Web アプリケーション であれば @sentry/browser@sentry/integrations をインストールすれば良いようです。

型定義 (@sentry/types) も提供されているので TypeScript でも利用できます。


基本

パッケージをインストールします。ここでインストールされるバージョンは 5.x の想定です。

npm i @sentry/browser @sentry/integrations

アプリケーションの出来るだけ早いタイミングで読み込み、 init() を実行します。


path/to/my/app.js

import { init } from '@sentry/browser'

import { Dedupe, ExtraErrorData } from '@sentry/integrations'

init({
dsn: 'https://foobar@sentry.io/1234567890',
integrations: [
new Dedupe(),
new ExtraErrorData({ depth: 3 })
]
})


init() が受け付ける引数は以下で定義されていますが、最低限必要なのは dsn だけなのかなと思います。

integrations に渡している Dedupe, ExtraErrorData はオプショナルになります。これらは @sentry/browser@4.x ではデフォルトで適用されていた物でしたが、 5.x からは明示的に登録してあげないと適用されません。

使い方や役割は こちらの公式ドキュメント を参照してください。

基本的には以上で完了ですが、利用している JS フレームワークによってはそれぞれ異なる integrations が必要になります。

下記は Vue.js の場合です。


path/to/my/app.js

import Vue from 'vue'

import * as Sentry from '@sentry/browser'
import * as Integrations from '@sentry/integrations'

Sentry.init({
dsn: 'https://foobar@sentry.io/1234567890',
integrations: [
new Dedupe(),
new ExtraErrorData({ depth: 3 }),
new Integrations.Vue({ Vue })
]
})


他のフレームワークの場合については公式ドキュメントから参照してください。

Docs - JavaScript


エラー発生時の認証ユーザー情報をログに含める

init() の実行よりも後に、 configureScope() のコールバック内で scope.setUser() を実行します。

(scope の定義はこちら: https://github.com/getsentry/sentry-javascript/blob/4.0.6/packages/hub/src/scope.ts)

import * as Sentry from '@sentry/browser'

const user = {
id: '1',
email: 'foo@bar.baz',
username: 'FooBar'
}

Sentry.init(...)

Sentry.configureScope(scope => {
scope.setUser( user )
})

この scope を使ってユーザー情報や Sentry のタグなどを設定できます。

例えば environment の値を設定する場合は次のようになります。

Sentry.configureScope(scope => {

scope.setTag('environment', 'production')
})

削除もできます。それまで configureScope() で設定された値が丸ごと消されるようです。

Sentry.configureScope(scope => {

// SPA でログアウト処理したら実行…とか?
scope.clear()

// 続けて次のスコープを設定したりも可能
scope.setUser(user2)
})

これはいい感じのラッパーを作って操作するのが良さそうですね。