エラーの扱い
アプリで起きたエラー(例外)を、記録したり、画面に出したりする方法を説明します。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 が見分けます。
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->report(function (InvalidOrderException $e) {
// ...
});
})
report で自分の記録の処理を登録しても、Laravel は、ふだんのログの設定でもその例外を記録します。ふだんのログに書かせたくないときは、記録の処理を決めるときに stop メソッドをつなぐか、クロージャから false を返します。
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 メソッドで、付け足す情報を自分で決めることもできます。この情報は、アプリが書く例外のログすべてに入ります。
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->context(fn () => [
'foo' => 'bar',
]);
})
例外ごとのログの情報#
すべてのログに情報を足すのも便利です。でも、ある例外にだけ関係する情報を、ログに入れたいこともあります。そのときは、アプリの例外のクラスに context メソッドを書いて、その例外のログに足したいデータを決めます。
<?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 ヘルパー関数(どこからでも呼べる便利な関数)なら、エラーページを出さずに、例外をすぐ記録できます。
public function isValid(string $value): bool
{
try {
// 値を確かめる
} catch (Throwable $e) {
report($e);
return false;
}
}
同じ例外を何度も記録しない#
アプリのあちこちで report を使うと、同じ例外を、何度も記録してしまうことがあります。ログに、同じ記録が重なります。
1つの例外は、1回しか記録しないようにしたいときは、bootstrap/app.php で、dontReportDuplicates メソッドを呼びます。
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontReportDuplicates();
})
こうすると、同じ例外のオブジェクトで report を呼んでも、最初の1回だけが記録されます。
$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つ目の引数がログレベルです。
use PDOException;
use Psr\Log\LogLevel;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->level(PDOException::class, LogLevel::CRITICAL);
})
種類で例外を無視する#
例外の種類によっては、いっさい記録したくないものもあります。bootstrap/app.php の dontReport メソッドで、無視できます。ここに渡したクラスの例外は、いっさい記録されません。ただし、画面に出す処理を自分で決めることは、これまでどおりできます。
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontReport([
InvalidOrderException::class,
]);
})
別の方法として、例外のクラスに、Illuminate\Contracts\Debug\ShouldntReport のインターフェイス(守るべきメソッドの決まり)で「印」を付けられます。この印を付けた例外は、Laravel の例外のハンドラ(扱う係)が、いっさい記録しません。
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;
class PodcastProcessingException extends Exception implements ShouldntReport
{
//
}
どんなときに例外を無視するかを、もっと細かく決めたいなら、dontReportWhen メソッドにクロージャを渡せます。
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 メソッドを使います。
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 が見分けます。
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 のふつうの出し方が使われます。
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 メソッドを使います。
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 メソッドで、レスポンスを変えるクロージャを登録できます。
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
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 レスポンスが使われます。
/**
* 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 を返します。
/**
* 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(くじのクラス)のオブジェクトを返すクロージャを渡します。
use Illuminate\Support\Lottery;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
return Lottery::odds(1, 1000);
});
})
例外の種類によって、条件を付けて選ぶこともできます。ある例外のクラスだけ選びたいなら、そのクラスのときだけ Lottery を返します。
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 のオブジェクトを返すと、ログに書く数や、外部のエラー記録のサービスに送る数に、回数の制限(レート制限)をかけられます。たとえば、アプリが使っている、ほかの会社のサービスが止まったとき、例外がどっと出て、ログがあふれるのを防げます。
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 メソッドで、このキーを自分で決められます。
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 を、まぜて返せます。
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 ヘルパー関数を使います。
abort(404);
自分で作る HTTP エラーページ#
Laravel では、HTTP ステータスコードごとに、自分のエラーページを出すのも簡単です。たとえば、404 のエラーページを変えるには、resources/views/errors/404.blade.php のビュー(画面の見た目を書いたファイル)を作ります。このビューは、アプリが作るすべての 404 エラーで出ます。このフォルダのビューには、HTTP ステータスコードと同じ名前を付けます。abort 関数が起こした Symfony\Component\HttpKernel\Exception\HttpException の例外は、$exception という変数でビューに渡されます。
<h2>{{ $exception->getMessage() }}</h2>
Laravel のふつうのエラーページのひな形は、vendor:publish の Artisan コマンド(php artisan で動かす Laravel のコマンド)で取り出せます。取り出したあとは、好きなように変えられます。
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日時点の内容をもとに、日本語でまとめています。