- Introducción
- Configuración
- El Manejador de Excepciones
- Limitando la Frecuencia de Reporte de Excepciones
- Excepciones HTTP
#Introducción
Cuando inicia un nuevo proyecto Laravel, el manejo de errores y excepciones ya está configurado para usted. La clase App\Exceptions\Handler es donde todas las excepciones lanzadas por su aplicación se registran y luego se muestran al usuario. Profundizaremos en esta clase a lo largo de esta documentación.
#Configuración
La opción debug en su archivo de configuración config/app.php determina cuánta información sobre un error se muestra realmente al usuario. Por defecto, esta opción está configurada para respetar el valor de la variable de entorno APP_DEBUG, que se almacena en su archivo .env.
Durante el desarrollo local, debe establecer la variable de entorno APP_DEBUG en true. En su entorno de producción, este valor siempre debe ser false. Si el valor está en true en producción, corre el riesgo de exponer valores de configuración sensibles a los usuarios finales de su aplicación.
#El Manejador de Excepciones
#Reportando Excepciones
Todas las excepciones son manejadas por la clase App\Exceptions\Handler. Esta clase contiene un método register donde puede registrar callbacks personalizados para reportar y renderizar excepciones. Examinaremos cada uno de estos conceptos en detalle. El reporte de excepciones se usa para registrar excepciones o enviarlas a un servicio externo como Flare, Bugsnag o Sentry. Por defecto, las excepciones se registran según su configuración de logging. Sin embargo, usted es libre de registrar excepciones como desee.
Si necesita reportar diferentes tipos de excepciones de distintas maneras, puede usar el método reportable para registrar un closure que se ejecutará cuando una excepción de un tipo dado necesite ser reportada. Laravel determinará qué tipo de excepción reporta el closure examinando la declaración de tipo del closure:
use App\Exceptions\InvalidOrderException;
/**
* Registrar los callbacks de manejo de excepciones para la aplicación.
*/
public function register(): void
{
$this->reportable(function (InvalidOrderException $e) {
// ...
});
}
Cuando registra un callback personalizado para reportar excepciones usando el método reportable, Laravel aún registrará la excepción usando la configuración de logging predeterminada para la aplicación. Si desea detener la propagación de la excepción al stack de logging predeterminado, puede usar el método stop al definir su callback de reporte o retornar false desde el callback:
$this->reportable(function (InvalidOrderException $e) {
// ...
})->stop();
$this->reportable(function (InvalidOrderException $e) {
return false;
});
Para personalizar el reporte de excepciones para una excepción dada, también puede utilizar excepciones reportables.
#Contexto Global de Registro
Si está disponible, Laravel añade automáticamente el ID del usuario actual a cada mensaje de registro de excepción como datos contextuales. Puede definir sus propios datos contextuales globales definiendo un método context en la clase App\Exceptions\Handler de su aplicación. Esta información se incluirá en cada mensaje de registro de excepción generado por su aplicación:
/**
* Obtener las variables de contexto predeterminadas para el logging.
*
* @return array<string, mixed>
*/
protected function context(): array
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}
#Contexto de Registro de Excepción
Aunque añadir contexto a cada mensaje de registro puede ser útil, a veces una excepción particular puede tener un contexto único que desea incluir en sus registros. Definiendo un método context en una de las excepciones de su aplicación, puede especificar cualquier dato relevante para esa excepción que debería añadirse a la entrada de registro de la excepción:
<?php
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* Obtener la información de contexto de la excepción.
*
* @return array<string, mixed>
*/
public function context(): array
{
return ['order_id' => $this->orderId];
}
}
#El Helper report
A veces puede necesitar reportar una excepción pero continuar manejando la solicitud actual. La función helper report le permite reportar rápidamente una excepción a través del manejador de excepciones sin renderizar una página de error para el usuario:
public function isValid(string $value): bool
{
try {
// Validar el valor...
} catch (Throwable $e) {
report($e);
return false;
}
}
#Dedupliación de Excepciones Reportadas
Si usa la función report en toda su aplicación, puede que ocasionalmente reporte la misma excepción varias veces, creando entradas duplicadas en sus registros.
Si desea asegurarse de que una única instancia de una excepción solo se reporte una vez, puede establecer la propiedad $withoutDuplicates en true dentro de la clase App\Exceptions\Handler de su aplicación:
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* Indica que una instancia de excepción solo debe ser reportada una vez.
*
* @var bool
*/
protected $withoutDuplicates = true;
// ...
}
Ahora, cuando se llame al helper report con la misma instancia de una excepción, solo la primera llamada será reportada:
$original = new RuntimeException('Whoops!');
report($original); // reportada
try {
throw $original;
} catch (Throwable $caught) {
report($caught); // ignorada
}
report($original); // ignorada
report($caught); // ignorada
#Niveles de Registro de Excepciones
Cuando se escriben mensajes en los logs de su aplicación, los mensajes se escriben en un nivel de log especificado, que indica la severidad o importancia del mensaje registrado.
Como se mencionó antes, incluso cuando registra un callback personalizado para reportar excepciones usando el método reportable, Laravel aún registrará la excepción usando la configuración de logging predeterminada para la aplicación; sin embargo, dado que el nivel de log puede influir en los canales donde se registra un mensaje, puede que desee configurar el nivel de log con el que ciertas excepciones se registran.
Para lograr esto, puede definir una propiedad $levels en el manejador de excepciones de su aplicación. Esta propiedad debe contener un array de tipos de excepción y sus niveles de log asociados:
use PDOException;
use Psr\Log\LogLevel;
/**
* Lista de tipos de excepción con sus niveles de log personalizados correspondientes.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
PDOException::class => LogLevel::CRITICAL,
];
#Ignorando Excepciones por Tipo
Al construir su aplicación, habrá algunos tipos de excepciones que nunca querrá reportar. Para ignorar estas excepciones, defina una propiedad $dontReport en el manejador de excepciones de su aplicación. Cualquier clase que agregue a esta propiedad nunca será reportada; sin embargo, aún pueden tener lógica personalizada para renderizar:
use App\Exceptions\InvalidOrderException;
/**
* Lista de tipos de excepción que no se reportan.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
InvalidOrderException::class,
];
Internamente, Laravel ya ignora algunos tipos de errores por usted, como excepciones resultantes de errores HTTP 404 o respuestas HTTP 419 generadas por tokens CSRF inválidos. Si desea indicarle a Laravel que deje de ignorar un tipo dado de excepción, puede invocar el método stopIgnoring dentro del método register de su manejador de excepciones:
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* Registrar los callbacks de manejo de excepciones para la aplicación.
*/
public function register(): void
{
$this->stopIgnoring(HttpException::class);
// ...
}
#Renderizando Excepciones
Por defecto, el manejador de excepciones de Laravel convertirá las excepciones en una respuesta HTTP para usted. Sin embargo, puede registrar un closure personalizado para renderizar excepciones de un tipo dado. Puede lograr esto invocando el método renderable dentro de su manejador de excepciones.
El closure pasado al método renderable debe devolver una instancia de Illuminate\Http\Response, que puede generarse mediante el helper response. Laravel determinará qué tipo de excepción renderiza el closure examinando la declaración de tipo del closure:
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;
/**
* Registrar los callbacks de manejo de excepciones para la aplicación.
*/
public function register(): void
{
$this->renderable(function (InvalidOrderException $e, Request $request) {
return response()->view('errors.invalid-order', [], 500);
});
}
También puede usar el método renderable para sobrescribir el comportamiento de renderizado para excepciones integradas de Laravel o Symfony como NotFoundHttpException. Si el closure dado al método renderable no devuelve un valor, se utilizará el renderizado de excepción predeterminado de Laravel:
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Registrar los callbacks de manejo de excepciones para la aplicación.
*/
public function register(): void
{
$this->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Registro no encontrado.'
], 404);
}
});
}
#Excepciones Reportables y Renderizables
En lugar de definir comportamiento personalizado para reporte y renderizado en el método register de su manejador de excepciones, puede definir métodos report y render directamente en las excepciones de su aplicación. Cuando estos métodos existen, serán llamados automáticamente por el framework:
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class InvalidOrderException extends Exception
{
/**
* Reportar la excepción.
*/
public function report(): void
{
// ...
}
/**
* Renderizar la excepción en una respuesta HTTP.
*/
public function render(Request $request): Response
{
return response(/* ... */);
}
}
Si su excepción extiende una excepción que ya es renderizable, como una excepción integrada de Laravel o Symfony, puede retornar false desde el método render de la excepción para renderizar la respuesta HTTP predeterminada de la excepción:
/**
* Renderizar la excepción en una respuesta HTTP.
*/
public function render(Request $request): Response|bool
{
if (/** Determinar si la excepción necesita renderizado personalizado */) {
return response(/* ... */);
}
return false;
}
Si su excepción contiene lógica personalizada de reporte que solo es necesaria cuando se cumplen ciertas condiciones, puede que necesite indicarle a Laravel que a veces reporte la excepción usando la configuración predeterminada de manejo de excepciones. Para lograr esto, puede retornar false desde el método report de la excepción:
/**
* Reportar la excepción.
*/
public function report(): bool
{
if (/** Determinar si la excepción necesita reporte personalizado */) {
// ...
return true;
}
return false;
}
Puede declarar cualquier dependencia requerida en el método report y serán inyectadas automáticamente por el contenedor de servicios de Laravel.
#Limitando la Frecuencia de Reporte de Excepciones
Si su aplicación reporta un número muy grande de excepciones, puede que desee limitar cuántas excepciones se registran o se envían al servicio externo de seguimiento de errores de su aplicación.
Para tomar una muestra aleatoria de excepciones, puede retornar una instancia de Lottery desde el método throttle de su manejador de excepciones. Si su clase App\Exceptions\Handler no contiene este método, simplemente puede agregarlo a la clase:
use Illuminate\Support\Lottery;
use Throwable;
/**
* Limitar la frecuencia de excepciones entrantes.
*/
protected function throttle(Throwable $e): mixed
{
return Lottery::odds(1, 1000);
}
También es posible muestrear condicionalmente según el tipo de excepción. Si desea muestrear solo instancias de una clase de excepción específica, puede retornar una instancia de Lottery solo para esa clase:
use App\Exceptions\ApiMonitoringException;
use Illuminate\Support\Lottery;
use Throwable;
/**
* Limitar la frecuencia de excepciones entrantes.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof ApiMonitoringException) {
return Lottery::odds(1, 1000);
}
}
También puede limitar la tasa de excepciones registradas o enviadas a un servicio externo de seguimiento de errores retornando una instancia de Limit en lugar de una Lottery. Esto es útil si desea protegerse contra ráfagas repentinas de excepciones que saturen sus registros, por ejemplo, cuando un servicio de terceros usado por su aplicación está caído:
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
/**
* Limitar la frecuencia de excepciones entrantes.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300);
}
}
Por defecto, los límites usarán la clase de la excepción como clave para la limitación de tasa. Puede personalizar esto especificando su propia clave usando el método by en el Limit:
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
/**
* Limitar la frecuencia de excepciones entrantes.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300)->by($e->getMessage());
}
}
Por supuesto, puede retornar una mezcla de instancias Lottery y Limit para diferentes excepciones:
use App\Exceptions\ApiMonitoringException;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Lottery;
use Throwable;
/**
* Limitar la frecuencia de excepciones entrantes.
*/
protected function throttle(Throwable $e): mixed
{
return match (true) {
$e instanceof BroadcastException => Limit::perMinute(300),
$e instanceof ApiMonitoringException => Lottery::odds(1, 1000),
default => Limit::none(),
};
}
#Excepciones HTTP
Algunas excepciones describen códigos de error HTTP del servidor. Por ejemplo, puede ser un error de "página no encontrada" (404), un error de "no autorizado" (401) o incluso un error 500 generado por el desarrollador. Para generar tal respuesta desde cualquier parte de su aplicación, puede usar el helper abort:
abort(404);
#Páginas de Error HTTP Personalizadas
Laravel facilita mostrar páginas de error personalizadas para varios códigos de estado HTTP. Por ejemplo, para personalizar la página de error para códigos de estado HTTP 404, cree una plantilla de vista resources/views/errors/404.blade.php. Esta vista se renderizará para todos los errores 404 generados por su aplicación. Las vistas dentro de este directorio deben nombrarse para coincidir con el código de estado HTTP al que corresponden. La instancia Symfony\Component\HttpKernel\Exception\HttpException generada por la función abort será pasada a la vista como una variable $exception:
<h2>{{ $exception->getMessage() }}</h2>
Puede publicar las plantillas de páginas de error predeterminadas de Laravel usando el comando Artisan vendor:publish. Una vez que las plantillas hayan sido publicadas, puede personalizarlas a su gusto:
php artisan vendor:publish --tag=laravel-errors
#Páginas de Error HTTP de Reserva
También puede definir una página de error "de reserva" para una serie dada de códigos de estado HTTP. Esta página se renderizará si no existe una página correspondiente para el código de estado HTTP específico que ocurrió. Para lograr esto, defina una plantilla 4xx.blade.php y una plantilla 5xx.blade.php en el directorio resources/views/errors de su aplicación.