本文へ移動
Laravel Tips

エラーの扱い

アプリで起きたエラー(例外)を、記録したり、画面に出したりする方法を説明します。APP_DEBUG・ログの記録・無視・表示の変更・HTTP エラーページまで引けます。

プログラムの動きの途中でうまくいかないことが起きると、「例外」(エラーを知らせるしくみ)が起きます。これを「例外を投げる」ともいいます。たとえば、ページが見つからない、データベースにつながらない、といったときです。エラーをどう扱うかは、「記録する」と「画面に出す」の2つに分けて考えます。

新しい Laravel のプロジェクトでは、エラーと例外の扱いは、最初から設定済みです。それでも、bootstrap/app.php の withExceptions メソッドを使えば、例外を記録する方法(report)や、画面に出す方法(render)を、いつでも変えられます。

withExceptions のクロージャ(名前のない関数)には、$exceptions が渡されます。これは Illuminate\Foundation\Configuration\Exceptions クラスの実体で、アプリの例外の扱いをまとめて管理する係です。このページでは、この $exceptions を何度も使います。

設定#

config/app.php の debug オプションは、エラーのくわしい情報を、見ている人にどこまで出すかを決めます。ふつう、この値は、.env(環境ごとに変える設定値を書くファイル)にある APP_DEBUG の環境変数に従います。

手元(ローカル)で開発するときは、APP_DEBUG を true にします。

注意

本番では、APP_DEBUG を、必ず false にします。本番で true のままだと、大事な設定の値が、アプリを使う人に見えてしまうおそれがあります。

例外を扱う#

例外を記録する(report)#

Laravel では、例外の「report」は、例外をログ(記録)に書くか、Laravel Nightwatch・Sentry・Flare のような外部のサービスへ送ることです。ふつう、例外は、ログの設定に従って記録されます。でも、好きな方法で記録してかまいません。

例外の種類ごとに記録のしかたを変えたいときは、bootstrap/app.php の report メソッドを使います。その種類の例外を記録するときに動くクロージャを登録します。どの種類の例外のためのクロージャかは、引数に書いた型を見て、Laravel が見分けます。

php
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // ...
    });
})

report で自分の記録の処理を登録しても、Laravel は、ふだんのログの設定でもその例外を記録します。ふだんのログに書かせたくないときは、記録の処理を決めるときに stop メソッドをつなぐか、クロージャから false を返します。

php
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // ...
    })->stop();

    $exceptions->report(function (InvalidOrderException $e) {
        return false;
    });
})

補足

ある例外の記録の仕方を変えるには、例外のクラスに直接書く方法もあります。下の「例外のクラスに report と render を書く」を見てください。

すべてのログに付ける情報#

Laravel は、いまログインしている人の ID が分かるときは、その ID を、例外のログのメッセージすべてに、付け足しの情報(コンテキスト)として自動で入れます。bootstrap/app.php の context メソッドで、付け足す情報を自分で決めることもできます。この情報は、アプリが書く例外のログすべてに入ります。

php
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->context(fn () => [
        'foo' => 'bar',
    ]);
})

例外ごとのログの情報#

すべてのログに情報を足すのも便利です。でも、ある例外にだけ関係する情報を、ログに入れたいこともあります。そのときは、アプリの例外のクラスに context メソッドを書いて、その例外のログに足したいデータを決めます。

php
<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    // ...

    /**
     * Get the exception's context information.
     *
     * @return array<string, mixed>
     */
    public function context(): array
    {
        return ['order_id' => $this->orderId];
    }
}

report ヘルパー関数#

例外を記録しても、いまのリクエストの処理は続けたいことがあります。report ヘルパー関数(どこからでも呼べる便利な関数)なら、エラーページを出さずに、例外をすぐ記録できます。

php
public function isValid(string $value): bool
{
    try {
        // 値を確かめる
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}

同じ例外を何度も記録しない#

アプリのあちこちで report を使うと、同じ例外を、何度も記録してしまうことがあります。ログに、同じ記録が重なります。

1つの例外は、1回しか記録しないようにしたいときは、bootstrap/app.php で、dontReportDuplicates メソッドを呼びます。

php
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportDuplicates();
})

こうすると、同じ例外のオブジェクトで report を呼んでも、最初の1回だけが記録されます。

php
$original = new RuntimeException('Whoops!');

report($original); // 記録される

try {
    throw $original;
} catch (Throwable $caught) {
    report($caught); // 無視される
}

report($original); // 無視される
report($caught); // 無視される

例外のログレベル#

アプリのログにメッセージを書くときは、決まったログレベル(メッセージの重さや大切さを表す段階)で書かれます。

上で説明したとおり、report で自分の記録の処理を登録しても、Laravel は、アプリのふつうのログの設定で、その例外を記録します。ただ、ログレベルによって、メッセージを書くチャンネル(書き込み先)が変わることがあります。そのため、ある例外のログレベルを変えたいこともあります。

bootstrap/app.php の level メソッドを使います。1つ目の引数が例外の種類、2つ目の引数がログレベルです。

php
use PDOException;
use Psr\Log\LogLevel;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->level(PDOException::class, LogLevel::CRITICAL);
})

種類で例外を無視する#

例外の種類によっては、いっさい記録したくないものもあります。bootstrap/app.php の dontReport メソッドで、無視できます。ここに渡したクラスの例外は、いっさい記録されません。ただし、画面に出す処理を自分で決めることは、これまでどおりできます。

php
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReport([
        InvalidOrderException::class,
    ]);
})

別の方法として、例外のクラスに、Illuminate\Contracts\Debug\ShouldntReport のインターフェイス(守るべきメソッドの決まり)で「印」を付けられます。この印を付けた例外は、Laravel の例外のハンドラ(扱う係)が、いっさい記録しません。

php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;

class PodcastProcessingException extends Exception implements ShouldntReport
{
    //
}

どんなときに例外を無視するかを、もっと細かく決めたいなら、dontReportWhen メソッドにクロージャを渡せます。

php
use App\Exceptions\InvalidOrderException;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportWhen(function (Throwable $e) {
        return $e instanceof PodcastProcessingException &&
               $e->reason() === 'Subscription expired';
    });
})

Laravel は、内部で、すでにいくつかの種類のエラーを無視しています。たとえば、404 の HTTP エラーの例外、リクエストの来た場所(オリジン)が合わないときの 403 のレスポンス、CSRF トークン(なりすましを防ぐ合言葉)が正しくないときの 419 のレスポンスです。ある種類の例外を無視しないようにするには、bootstrap/app.php の stopIgnoring メソッドを使います。

php
use Symfony\Component\HttpKernel\Exception\HttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->stopIgnoring(HttpException::class);
})

例外を画面に出す(render)#

ふつう、Laravel の例外のハンドラは、例外を、HTTP レスポンスに変えてくれます。でも、ある種類の例外について、画面に出す処理(render のクロージャ)を自分で書くこともできます。bootstrap/app.php の render メソッドを使います。

render に渡すクロージャは、Illuminate\Http\Response を返すようにします。このレスポンスは response ヘルパー関数で作れます。どの種類の例外を扱うかは、クロージャの引数の型で、Laravel が見分けます。

php
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });
})

render は、NotFoundHttpException のような、Laravel や Symfony に最初からある例外の、画面の出し方を上書きするのにも使えます。render のクロージャが何も返さなければ、Laravel のふつうの出し方が使われます。

php
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.'
            ], 404);
        }
    });
})

例外を JSON で出す#

例外を出すとき、Laravel は、リクエストの Accept ヘッダー(相手が受け取れるデータの種類)を見て、HTML で出すか、JSON(データを文字で表す形式)で出すかを、自動で決めます。この決め方を変えたいときは、shouldRenderJsonWhen メソッドを使います。

php
use Illuminate\Http\Request;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        if ($request->is('admin/*')) {
            return true;
        }

        return $request->expectsJson();
    });
})

例外のレスポンスをまるごと変える#

まれに、Laravel の例外のハンドラが作った HTTP レスポンスを、まるごと変えたいことがあります。respond メソッドで、レスポンスを変えるクロージャを登録できます。

php
use Symfony\Component\HttpFoundation\Response;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->respond(function (Response $response) {
        if ($response->getStatusCode() === 419) {
            return back()->with([
                'message' => 'The page expired, please try again.',
            ]);
        }

        return $response;
    });
})

例外のクラスに report と render を書く#

bootstrap/app.php に、記録と画面の出し方を書く代わりに、アプリの例外のクラスに、report と render のメソッド(クラスの中の関数)を、直接書けます。これらのメソッドがあれば、Laravel が自動で呼びます。

php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class InvalidOrderException extends Exception
{
    /**
     * Report the exception.
     */
    public function report(): void
    {
        // ...
    }

    /**
     * Render the exception as an HTTP response.
     */
    public function render(Request $request): Response
    {
        return response(/* ... */);
    }
}

自分の例外のクラスが、もともと画面の出し方を持っている例外(Laravel や Symfony に最初からある例外)を引き継いでいることがあります。そのときは、render メソッドから false を返すと、もとの例外のふつうの HTTP レスポンスが使われます。

php
/**
 * Render the exception as an HTTP response.
 */
public function render(Request $request): Response|bool
{
    if (/** Determine if the exception needs custom rendering */) {

        return response(/* ... */);
    }

    return false;
}

例外の report メソッドに、ある条件のときだけ要る記録の処理を書くことがあります。そうすると、条件に合わないときは、ふだんの設定どおりに記録してほしくなります。そのときは、report メソッドから false を返します。

php
/**
 * Report the exception.
 */
public function report(): bool
{
    if (/** Determine if the exception needs custom reporting */) {

        // ...

        return true;
    }

    return false;
}

補足

report メソッドの引数に、必要な部品の型を書けば、Laravel のサービスコンテナ(クラスを作って渡してくれる道具箱)が、自動で渡してくれます。

記録する例外の数を絞る#

アプリがとても多くの例外を記録するなら、実際にログに書く数や、外部のエラー記録のサービスに送る数を、絞りたいことがあります。

例外の一部だけをランダムに選んで記録する(サンプリングする)には、bootstrap/app.php の throttle メソッドを使います。throttle には、Lottery(くじのクラス)のオブジェクトを返すクロージャを渡します。

php
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        return Lottery::odds(1, 1000);
    });
})

例外の種類によって、条件を付けて選ぶこともできます。ある例外のクラスだけ選びたいなら、そのクラスのときだけ Lottery を返します。

php
use App\Exceptions\ApiMonitoringException;
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof ApiMonitoringException) {
            return Lottery::odds(1, 1000);
        }
    });
})

Lottery の代わりに Limit のオブジェクトを返すと、ログに書く数や、外部のエラー記録のサービスに送る数に、回数の制限(レート制限)をかけられます。たとえば、アプリが使っている、ほかの会社のサービスが止まったとき、例外がどっと出て、ログがあふれるのを防げます。

php
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof BroadcastException) {
            return Limit::perMinute(300);
        }
    });
})

ふつう、制限は例外のクラスごとに数えます。例外のクラスを、数える単位(キー)にするということです。Limit の by メソッドで、このキーを自分で決められます。

php
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof BroadcastException) {
            return Limit::perMinute(300)->by($e->getMessage());
        }
    });
})

もちろん、例外ごとに、Lottery と Limit を、まぜて返せます。

php
use App\Exceptions\ApiMonitoringException;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        return match (true) {
            $e instanceof BroadcastException => Limit::perMinute(300),
            $e instanceof ApiMonitoringException => Lottery::odds(1, 1000),
            default => Limit::none(),
        };
    });
})

HTTP の例外#

例外には、サーバーの HTTP エラーコード(エラーの種類を表す番号)を表すものがあります。たとえば、「ページが見つからない」の 404、「権限がない」の 401、開発者が自分で作る 500 などです。アプリのどこからでも、こうしたレスポンスを作りたいときは、abort ヘルパー関数を使います。

php
abort(404);

自分で作る HTTP エラーページ#

Laravel では、HTTP ステータスコードごとに、自分のエラーページを出すのも簡単です。たとえば、404 のエラーページを変えるには、resources/views/errors/404.blade.php のビュー(画面の見た目を書いたファイル)を作ります。このビューは、アプリが作るすべての 404 エラーで出ます。このフォルダのビューには、HTTP ステータスコードと同じ名前を付けます。abort 関数が起こした Symfony\Component\HttpKernel\Exception\HttpException の例外は、$exception という変数でビューに渡されます。

blade
<h2>{{ $exception->getMessage() }}</h2>

Laravel のふつうのエラーページのひな形は、vendor:publish の Artisan コマンド(php artisan で動かす Laravel のコマンド)で取り出せます。取り出したあとは、好きなように変えられます。

bash
php artisan vendor:publish --tag=laravel-errors

予備の HTTP エラーページ#

ある範囲の HTTP ステータスコードに、「予備(フォールバック)」のエラーページを決めることもできます。そのステータスコード専用のページがないときに、このページが出ます。resources/views/errors フォルダに、4xx.blade.php と 5xx.blade.php のテンプレートを作ります。

予備のエラーページは、401・402・403・404・419・429・500・503 のエラーレスポンスには、影響しません。この番号には、Laravel の中に、専用のページがあるからです。この番号で出すページを変えたいときは、番号ごとに、自分のエラーページを作ります。

エラーの扱いのメソッド一覧#

このページで使った、$exceptions のメソッドをまとめます。

メソッド 説明
report 例外の種類ごとに、記録の処理を決める
stop report の記録のあと、ふつうのログへ渡さない(report につなぐ)
context すべての例外のログに付ける情報を決める
dontReportDuplicates 同じ例外のオブジェクトを、1回しか記録しない
level 例外の種類ごとに、ログレベルを決める
dontReport 指定した種類の例外を、記録しない
dontReportWhen クロージャが true を返したときは、記録しない
stopIgnoring Laravel が最初から無視している種類を、無視しないようにする
render 例外の種類ごとに、画面の出し方を決める
shouldRenderJsonWhen JSON で出すか、HTML で出すかの決め方を変える
respond 例外のレスポンスを、まるごと変える
throttle 記録する例外の数を絞る(Lottery か Limit を返す)

関連するページ#

公式ドキュメント(英語)

2026年10月5日時点の内容をもとに、日本語でまとめています。

ページの一覧