Обробка Помилок
Вступ
Коли ви починаєте новий проект Laravel, обробка помилок та винятків вже налаштована для вас; однак, у будь-який момент ви можете використовувати метод withExceptions у файлі bootstrap/app.php вашого застосунку, щоб керувати тим, як винятки повідомляються та відображаються вашим застосунком.
Об'єкт $exceptions, наданий для замикання withExceptions, є екземпляром Illuminate\Foundation\Configuration\Exceptions і відповідає за управління обробкою винятків у вашому застосунку. Ми детальніше розглянемо цей об'єкт у цій документації.
Конфігурація
Опція debug у вашому конфігураційному файлі config/app.php визначає, скільки інформації про помилку фактично відображається користувачеві. За замовчуванням ця опція налаштована на врахування значення змінної середовища APP_DEBUG, яка зберігається у вашому файлі .env.
Під час локальної розробки ви повинні встановити змінну середовища APP_DEBUG на true. У вашому виробничому середовищі це значення завжди повинно бути false. Якщо значення встановлено на true у виробничому середовищі, ви ризикуєте розкрити конфіденційні значення конфігурації кінцевим користувачам вашого застосунку.
Обробка Винятків
Повідомлення про виняток
У Laravel звітування про винятки використовується для реєстрації винятків або їх відправки до зовнішнього сервісу, такого як Sentry або Flare. За замовчуванням винятки будуть реєструватися на основі вашої конфігурації логування. Однак ви можете реєструвати винятки так, як вам зручно.
Якщо вам потрібно повідомляти про різні типи винятків по-різному, ви можете використовувати метод винятку report у файлі bootstrap/app.php вашого застосунку, щоб зареєструвати замикання, яке має бути виконане, коли потрібно повідомити про виняток певного типу. Laravel визначить, про який тип винятку повідомляє замикання, перевіряючи тип підказки замикання:
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (InvalidOrderException $e) {
// ...
});
})
Коли ви реєструєте власний зворотний виклик для звітування про виключення, використовуючи метод report, Laravel все ще буде реєструвати виключення, використовуючи конфігурацію журналювання за замовчуванням для застосунку. Якщо ви бажаєте зупинити поширення виключення до стеку журналювання за замовчуванням, ви можете використовувати метод stop при визначенні вашого зворотного виклику для звітування або повернути false з цього зворотного виклику:
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (InvalidOrderException $e) {
// ...
})->stop();
$exceptions->report(function (InvalidOrderException $e) {
return false;
});
})
Щоб налаштувати звітування про винятки для даного винятку, ви також можете скористатися винятками, що підлягають звітуванню.
Глобальний Контекст Логів
Якщо доступно, Laravel автоматично додає ID поточного користувача до кожного повідомлення журналу винятків як контекстні дані. Ви можете визначити власні глобальні контекстні дані, використовуючи метод винятку context у файлі bootstrap/app.php вашого застосунку. Ця інформація буде включена в кожне повідомлення журналу винятків, записане вашим застосунком:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->context(fn () => [
'foo' => 'bar',
]);
})
Контекст Журналу Винятків
Хоча додавання контексту до кожного журналу може бути корисним, іноді певний виняток може мати унікальний контекст, який ви хотіли б включити у ваші журнали. Визначивши метод context у одному з винятків вашого застосунку, ви можете вказати будь-які дані, що стосуються цього винятку, які слід додати до запису журналу винятку:
<?php
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* Отримати інформацію про контекст винятку.
*
* @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 у вашому застосунку, ви можете час від часу повідомляти про одну й ту ж саму виняткову ситуацію кілька разів, створюючи дублікати записів у ваших журналах.
Якщо ви хочете переконатися, що один екземпляр винятку повідомляється лише один раз, ви можете викликати метод винятку dontReportDuplicates у файлі bootstrap/app.php вашого застосунку:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->dontReportDuplicates();
})
Тепер, коли викликається хелпер report з тим самим екземпляром винятку, буде зареєстровано лише перший виклик:
$original = new RuntimeException('Упс!'); report($original); // reported try { throw $original; } catch (Throwable $caught) { report($caught); // ignored } report($original); // ignored report($caught); // ignored
Рівні Логування Винятків
Коли повідомлення записуються у журнали вашого застосунку, повідомлення записуються на вказаному рівні журналу, що вказує на серйозність або важливість повідомлення, яке записується.
Як зазначено вище, навіть коли ви реєструєте власний зворотний виклик для звітування про винятки за допомогою методу report, Laravel все одно буде реєструвати виняток, використовуючи конфігурацію журналювання за замовчуванням для застосунку; однак, оскільки рівень журналювання іноді може впливати на канали, на яких повідомлення реєструється, ви можете захотіти налаштувати рівень журналювання, на якому певні винятки реєструються.
Щоб досягти цього, ви можете використовувати метод виключення level у файлі bootstrap/app.php вашого застосунку. Цей метод отримує тип виключення як перший аргумент і рівень журналу як другий аргумент:
use PDOException;
use Psr\Log\LogLevel;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->level(PDOException::class, LogLevel::CRITICAL);
})
Ігнорування винятків за типом
Коли ви створюєте свій застосунок, існують деякі типи винятків, які ви ніколи не хочете повідомляти. Щоб ігнорувати ці винятки, ви можете використовувати метод винятків dontReport у файлі bootstrap/app.php вашого застосунку. Будь-який клас, наданий цьому методу, ніколи не буде повідомлений; однак, вони все ще можуть мати власну логіку відображення:
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions) {
$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
{
//
}
Внутрішньо Laravel вже ігнорує деякі типи помилок для вас, такі як виключення, що виникають в результаті 404 HTTP помилок або 419 HTTP відповідей, згенерованих через недійсні CSRF токени. Якщо ви хочете вказати Laravel припинити ігнорувати певний тип виключення, ви можете використовувати метод виключення stopIgnoring у файлі bootstrap/app.php вашого застосунку:
use Symfony\Component\HttpKernel\Exception\HttpException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->stopIgnoring(HttpException::class);
})
Відображення Винятків
За замовчуванням обробник виключень Laravel перетворює виключення у HTTP-відповідь для вас. Однак ви можете зареєструвати власне замикання для рендерингу виключень певного типу. Ви можете досягти цього, використовуючи метод виключення render у файлі bootstrap/app.php вашого застосунку.
Замикання, передане методу 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', status: 500);
});
})
Ви також можете використовувати метод render для перевизначення поведінки рендерингу для вбудованих винятків Laravel або Symfony, таких як NotFoundHttpException. Якщо замикання, передане методу 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' => 'Запис не знайдено.' ], 404); } }); })
Відображення винятків у форматі JSON
Коли відбувається рендеринг виключення, Laravel автоматично визначить, чи слід рендерити виключення як HTML або JSON відповідь, на основі заголовка запиту Accept. Якщо ви хочете налаштувати, як 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();
});
})
Налаштування відповіді на виключення
Рідко, але іноді може виникнути потреба налаштувати весь HTTP-відповідь, що відображається обробником винятків Laravel. Для цього ви можете зареєструвати замикання для налаштування відповіді, використовуючи метод respond:
use Symfony\Component\HttpFoundation\Response; ->withExceptions(function (Exceptions $exceptions) { $exceptions->respond(function (Response $response) { if ($response->getStatusCode() === 419) { return back()->with([ 'message' => 'Сторінка застаріла, будь ласка, спробуйте ще раз.', ]); } return $response; }); })
Винятки, що підлягають звітуванню та відображенню
Замість визначення власної поведінки звітування та відображення у файлі bootstrap/app.php вашого застосунку, ви можете визначити методи report та render безпосередньо у виключеннях вашого застосунку. Коли ці методи існують, вони автоматично викликатимуться фреймворком:
<?php namespace App\Exceptions; use Exception; use Illuminate\Http\Request; use Illuminate\Http\Response; class InvalidOrderException extends Exception { /** * Повідомити про виняток. */ public function report(): void { // ... } /** * Відобразити виняток як HTTP-відповідь. */ public function render(Request $request): Response { return response(/* ... */); } }
Якщо ваш виняток розширює виняток, який вже може бути відображений, наприклад, вбудований виняток Laravel або Symfony, ви можете повернути false з методу render винятку, щоб відобразити стандартну HTTP-відповідь винятку:
/** * Відобразити виняток як HTTP-відповідь. */ public function render(Request $request): Response|bool { if (/** Визначити, чи потребує виняток власного відображення. */) { return response(/* ... */); } return false; }
Якщо ваш виняток містить логіку звітування, яка необхідна лише за певних умов, можливо, вам потрібно буде вказати Laravel іноді повідомляти про виняток, використовуючи конфігурацію обробки винятків за замовчуванням. Щоб досягти цього, ви можете повернути false з методу report винятку:
/** * Повідомити про виняток. */ public function report(): bool { if (/**Визначити, чи потребує виняток власного звітування. */) { // ... return true; } return false; }
Ви можете вказати тип для будь-яких необхідних залежностей методу report, і вони будуть автоматично впроваджені в метод за допомогою Сервіс-контейнера Laravel.
Обмеження частоти звітів про винятки
Якщо ваш застосунок повідомляє про дуже велику кількість винятків, можливо, ви захочете обмежити кількість винятків, які фактично реєструються або надсилаються до зовнішньої служби відстеження помилок вашого застосунку.
Щоб взяти випадкову вибірку винятків, ви можете використовувати метод винятків throttle у файлі bootstrap/app.php вашого застосунку. Метод throttle отримує замикання, яке повинно повертати екземпляр Lottery:
use Illuminate\Support\Lottery;
use Throwable;
->withExceptions(function (Exceptions $exceptions) {
$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) {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof ApiMonitoringException) {
return Lottery::odds(1, 1000);
}
});
})
Ви також можете обмежити винятки, що записуються в журнал або надсилаються до зовнішньої служби відстеження помилок, повертаючи екземпляр Limit замість Lottery. Це корисно, якщо ви хочете захиститися від раптових сплесків винятків, що заповнюють ваші журнали, наприклад, коли сторонній сервіс, що використовується вашим застосунком, не працює:
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof BroadcastException) {
return Limit::perMinute(300);
}
});
})
За замовчуванням обмеження використовуватимуть клас виключення як ключ обмеження швидкості. Ви можете налаштувати це, вказавши власний ключ за допомогою методу by на Limit:
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
->withExceptions(function (Exceptions $exceptions) {
$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) {
$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 HTTP статус-кодів, створіть шаблон представлення resources/views/errors/404.blade.php. Це представлення буде відображатися для всіх 404 помилок, згенерованих вашим застосунком. Представлення в цій директорії повинні бути названі відповідно до HTTP статус-коду, якому вони відповідають. Екземпляр Symfony\Component\HttpKernel\Exception\HttpException, викликаний функцією abort, буде переданий до представлення як змінна $exception:
<h2>{{ $exception->getMessage() }}</h2>
Ви можете опублікувати шаблони сторінок помилок за замовчуванням Laravel, використовуючи команду Artisan vendor:publish. Після того, як шаблони будуть опубліковані, ви можете налаштувати їх на свій розсуд:
php artisan vendor:publish --tag=laravel-errors
Резервні сторінки помилок HTTP
Ви також можете визначити "резервну" сторінку помилки для певної серії кодів стану HTTP. Ця сторінка буде відображена, якщо немає відповідної сторінки для конкретного коду стану HTTP, що виник. Щоб це реалізувати, визначте шаблон 4xx.blade.php і шаблон 5xx.blade.php у директорії resources/views/errors вашого застосунку.
Коли визначаються резервні сторінки помилок, резервні сторінки не впливатимуть на відповіді помилок 404, 500 і 503, оскільки Laravel має внутрішні, спеціальні сторінки для цих кодів статусу. Щоб налаштувати сторінки, що відображаються для цих кодів статусу, ви повинні визначити індивідуальну сторінку помилки для кожного з них окремо.
