エラー処理
はじめに
新しいLaravelプロジェクトを開始すると、エラーと例外処理はすでに設定されています。ただし、いつでもアプリケーションのbootstrap/app.php
でwithExceptions
メソッドを使用して、例外がアプリケーションによって報告およびレンダリングされる方法を管理できます。
withExceptions
クロージャに提供される$exceptions
オブジェクトは、Illuminate\Foundation\Configuration\Exceptions
のインスタンスであり、アプリケーション内の例外処理を管理する責任があります。このドキュメント全体でこのオブジェクトについて詳しく説明します。
構成
config/app.php
構成ファイル内のdebug
オプションは、エラーに関する情報が実際にユーザーに表示される量を決定します。デフォルトでは、このオプションは.env
ファイルに保存されているAPP_DEBUG
環境変数の値を尊重するように設定されています。
ローカル開発中は、APP_DEBUG
環境変数をtrue
に設定する必要があります。本番環境では、この値は常にfalse
に設定する必要があります。本番環境でtrue
に設定すると、アプリケーションのエンドユーザーに機密な構成値が公開されるリスクがあります。
例外の処理
例外の報告
Laravelでは、例外の報告は例外をログに記録したり、外部サービスSentryやFlareに送信するために使用されます。デフォルトでは、例外はログ構成に基づいて記録されます。ただし、例外を好きなようにログに記録することができます。
異なる種類の例外を異なる方法で報告する必要がある場合は、アプリケーションの bootstrap/app.php
で report
例外メソッドを使用して、特定のタイプの例外が報告される必要があるときに実行されるべきクロージャを登録できます。Laravel は、クロージャの型ヒントを調べることで、クロージャが報告する例外のタイプを決定します:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (InvalidOrderException $e) {
// ...
});
})
report
メソッドを使用してカスタム例外報告コールバックを登録すると、Laravel はアプリケーションのデフォルトのロギング構成を使用して例外をログに記録します。例外をデフォルトのロギングスタックに伝播させるのを停止したい場合は、報告コールバックを定義する際に stop
メソッドを使用するか、コールバックから false
を返すことができます:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (InvalidOrderException $e) {
// ...
})->stop();
$exceptions->report(function (InvalidOrderException $e) {
return false;
});
})
特定の例外の例外報告をカスタマイズするには、報告可能な例外 も利用できます。
グローバルログコンテキスト
利用可能な場合、Laravel は現在のユーザー ID を各例外のログメッセージにコンテキストデータとして自動的に追加します。アプリケーションの bootstrap/app.php
ファイルで context
例外メソッドを使用して、独自のグローバルコンテキストデータを定義できます。この情報は、アプリケーションによって書かれた各例外のログメッセージに含まれます:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->context(fn () => [
'foo' => 'bar',
]);
})
例外ログコンテキスト
すべてのログメッセージにコンテキストを追加することは便利ですが、特定の例外にはログに含めたい固有のコンテキストがある場合があります。アプリケーションの例外の1つに 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 {
// Validate the value...
} catch (Throwable $e) {
report($e);
return false;
}
}
重複した報告された例外の排除
アプリケーション全体で report
関数を使用している場合、同じ例外を複数回報告することがあり、ログに重複したエントリが作成される可能性があります。
例外のインスタンスが一度だけ報告されることを保証したい場合は、アプリケーションの bootstrap/app.php
ファイルで dontReportDuplicates
例外メソッドを呼び出すことができます:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->dontReportDuplicates();
})
これで、同じ例外のインスタンスで report
ヘルパーが呼び出された場合、最初の呼び出しだけが報告されます:
$original = new RuntimeException('Whoops!');
report($original); // reported
try {
throw $original;
} catch (Throwable $caught) {
report($caught); // ignored
}
report($original); // ignored
report($caught); // ignored
例外ログレベル
アプリケーションの ログ にメッセージが書き込まれると、メッセージは指定された ログレベル で書き込まれます。これは、ログに記録されるメッセージの深刻さや重要性を示します。
前述のように、report
メソッドを使用してカスタム例外報告コールバックを登録しても、Laravel はアプリケーションのデフォルトのログ設定を使用して例外をログに記録します。ただし、ログレベルはメッセージが記録されるチャンネルに影響 を与えることがあるため、特定の例外がログに記録されるログレベルを設定したい場合があります。
これを実現するために、アプリケーションの bootstrap/app.php
ファイルで level
例外メソッドを使用できます。このメソッドは、例外のタイプを最初の引数として、ログレベルを2番目の引数として受け取ります:
use PDOException;
use Psr\Log\LogLevel;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->level(PDOException::class, LogLevel::CRITICAL);
})
タイプ別の例外の無視
アプリケーションを構築する際、報告したくない種類の例外がいくつかあります。これらの例外を無視するには、アプリケーションの bootstrap/app.php
ファイルで dontReport
例外メソッドを使用できます。このメソッドに提供されたクラスは報告されませんが、カスタムのレンダリングロジックを持つ場合があります:
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->dontReport([
InvalidOrderException::class,
]);
})
内部的に、Laravel は既に404 HTTP エラーや無効な CSRF トークンによって生成された 419 HTTP レスポンスから発生する例外など、一部のエラータイプを無視しています。特定の種類の例外の無視を停止するよう Laravel に指示したい場合は、アプリケーションの bootstrap/app.php
ファイルで stopIgnoring
例外メソッドを使用できます:
use Symfony\Component\HttpKernel\Exception\HttpException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->stopIgnoring(HttpException::class);
})
例外のレンダリング
デフォルトでは、Laravelの例外ハンドラは例外をHTTPレスポンスに変換します。ただし、特定のタイプの例外に対してカスタムのレンダリングクロージャを登録することもできます。これは、アプリケーションの bootstrap/app.php
ファイルで render
例外メソッドを使用することで実現できます。
render
メソッドに渡されるクロージャは、Illuminate\Http\Response
のインスタンスを返す必要があります。これは response
ヘルパーを使用して生成することができます。Laravelは、クロージャの型ヒントを調べることで、クロージャがどのタイプの例外をレンダリングするかを決定します:
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (InvalidOrderException $e, Request $request) {
return response()->view('errors.invalid-order', [], 500);
});
})
render
メソッドを使用して、NotFoundHttpException
などの組み込みの Laravel または Symfony 例外のレンダリング動作をオーバーライドすることもできます。render
メソッドに与えられたクロージャが値を返さない場合、Laravelのデフォルトの例外レンダリングが利用されます:
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
})
JSONとしての例外のレンダリング
例外をレンダリングする際、Laravelはリクエストの Accept
ヘッダに基づいて例外をHTMLまたはJSONレスポンスとしてレンダリングすべきかを自動的に判断します。LaravelがHTMLまたはJSON例外レスポンスをレンダリングするかどうかをカスタマイズしたい場合は、shouldRenderJsonWhen
メソッドを使用できます:
use Illuminate\Http\Request;
use Throwable;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
if ($request->is('admin/*')) {
return true;
}
return $request->expectsJson();
});
})