1. Conceptos básicos
  2. Manejo de Errores

Introducción

Cuando inicias un nuevo proyecto Laravel, el manejo de errores y excepciones ya está configurado para ti. La clase App\Exceptions\Handler es donde se registran todas las excepciones lanzadas por tu aplicación y luego se representan al usuario. Profundizaremos más en esta clase a lo largo de esta documentación.

Configuración

La opción debug en tu 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 tu archivo .env.

Durante el desarrollo local, debes establecer la variable de entorno APP_DEBUG en true. En tu entorno de producción, este valor siempre debe ser false. Si el valor se establece en true en producción, corres el riesgo de exponer valores de configuración sensibles a los usuarios finales de tu aplicación.

El Manipulador de Excepciones

Informar Excepciones

Todas las excepciones son manejadas por la clase App\Exceptions\Handler. Esta clase contiene un método register donde puedes registrar devoluciones de llamada personalizadas para informe y representación de excepciones. Examinaremos cada uno de estos conceptos en detalle. La notificación de excepciones se utiliza para registrar excepciones o enviarlas a un servicio externo como Flare, Bugsnag o Sentry. Por defecto, las excepciones se registrarán según la configuración de registros. Sin embargo, eres libre de registrar excepciones como desees.

Si necesitas informar diferentes tipos de excepciones de diferentes maneras, puedes usar el método reportable para registrar un cierre que se debe ejecutar cuando se debe informar de una excepción de un tipo dado. Laravel determinará qué tipo de excepción informa el cierre examinando la sugerencia de tipo del cierre:

use App\Exceptions\InvalidOrderException;
 
/**
* Registra las devoluciones de llamada para el manejo de excepciones de la aplicación.
*/
public function register(): void
{
$this->reportable(function (InvalidOrderException $e) {
// ...
});
}

Cuando registras una devolución de llamada de informe de excepción personalizada utilizando el método reportable, Laravel seguirá registrando la excepción utilizando la configuración de registro predeterminada de la aplicación. Si deseas detener la propagación de la excepción en la pila de registro predeterminada, puedes usar el método stop al definir tu devolución de llamada de informe o devolver false desde la devolución de llamada:

$this->reportable(function (InvalidOrderException $e) {
// ...
})->stop();
 
$this->reportable(function (InvalidOrderException $e) {
return false;
});

Nota Para personalizar la notificación de excepciones para una excepción dada, también puedes utilizar excepciones reportables.

Contexto Global de Registro

Si está disponible, Laravel agrega automáticamente el ID del usuario actual a cada mensaje de registro de excepción como datos contextuales. Puedes definir tus propios datos contextuales globales al definir un método context en la clase App\Exceptions\Handler de tu aplicación. Esta información se incluirá en cada mensaje de registro de excepción escrito por tu aplicación:

/**
* Obtiene las variables de contexto predeterminadas para el registro.
*
* @return array<string, mixed>
*/
protected function context(): array
{
return array_merge(parent::context(), [
'foo' => 'bar',
]);
}

Contexto de Registro de Excepciones

Si bien agregar contexto a cada mensaje de registro puede ser útil, a veces una excepción en particular puede tener un contexto único que te gustaría incluir en tus registros. Al definir un método context en una de las excepciones de tu aplicación, puedes especificar cualquier dato relevante para esa excepción que deba agregarse a la entrada de registro de la excepción:

<?php
 
namespace App\Exceptions;
 
use Exception;
 
class InvalidOrderException extends Exception
{
// ...
 
/**
* Obtiene la información de contexto de la excepción.
*
* @return array<string, mixed>
*/
public function context(): array
{
return ['order_id' => $this->orderId];
}
}

El ayudante report

En ocasiones, es posible que necesites informar de una excepción pero continuar manejando la solicitud actual. La función auxiliar report te permite informar rápidamente de una excepción a través del manejador de excepciones sin renderizar una página de error al usuario:

public function isValid(string $value): bool
{
try {
// Valida el valor...
} catch (Throwable $e) {
report($e);
 
return false;
}
}

Deduplicación de Excepciones Informadas

Si estás utilizando la función report en toda tu aplicación, es posible que ocasionalmente informes la misma excepción varias veces, lo que crea entradas duplicadas en tus registros.

Si deseas asegurarte de que solo se informa una sola instancia de una excepción, puedes establecer la propiedad $withoutDuplicates en true en la clase App\Exceptions\Handler de tu 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 informarse una vez.
*
* @var bool
*/
protected $withoutDuplicates = true;
 
// ...
}

Ahora, cuando la función report se llama con la misma instancia de una excepción, solo se informará la primera llamada:

$original = new RuntimeException('Whoops!');
 
report($original); // reported
 
try {
throw $original;
} catch (Throwable $caught) {
report($caught); // ignored
}
 
report($original); // ignored
report($caught); // ignored

Niveles de Registro de Excepciones

Cuando se escriben mensajes en los registros de tu aplicación, los mensajes se escriben en un nivel de registro especificado, que indica la gravedad o importancia del mensaje que se está registrando.

Como se mencionó anteriormente, incluso cuando registras una devolución de llamada de informe de excepción personalizada utilizando el método reportable, Laravel seguirá registrando la excepción utilizando la configuración de registro predeterminada de la aplicación; sin embargo, dado que el nivel de registro a veces puede influir en los canales en los que se registra un mensaje, es posible que desees configurar el nivel de registro en el que se registran ciertas excepciones.

Para lograr esto, puedes definir una propiedad $levels en el manejador de excepciones de tu aplicación. Esta propiedad debe contener una matriz de tipos de excepciones y sus niveles de registro asociados:

use PDOException;
use Psr\Log\LogLevel;
 
/**
* Una lista de tipos de excepciones con sus niveles de registro personalizados correspondientes.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
PDOException::class => LogLevel::CRITICAL,
];

Ignorar Excepciones por Tipo

Al construir tu aplicación, habrá algunos tipos de excepciones que nunca querrás informar. Para ignorar estas excepciones, define una propiedad $dontReport en el manejador de excepciones de tu aplicación. Cualquier clase que agregues a esta propiedad nunca se informará; sin embargo, aún pueden tener lógica de representación personalizada:

use App\Exceptions\InvalidOrderException;
 
/**
* Una lista de tipos de excepciones que no se informan.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
InvalidOrderException::class,
];

Internamente, Laravel ya ignora algunos tipos de errores para ti, como excepciones resultantes de errores HTTP 404 o respuestas HTTP 419 generadas por tokens CSRF no válidos. Si deseas indicarle a Laravel que deje de ignorar un tipo dado de excepción, puedes invocar el método stopIgnoring dentro del método register de tu manejador de excepciones:

use Symfony\Component\HttpKernel\Exception\HttpException;
 
/**
* Registra las devoluciones de llamada para el manejo de excepciones de la aplicación.
*/
public function register(): void
{
$this->stopIgnoring(HttpException::class);
 
// ...
}

Representar Excepciones

Por defecto, el manejador de excepciones de Laravel convertirá las excepciones en una respuesta HTTP por ti. Sin embargo, eres libre de registrar un cierre de representación personalizado para excepciones de un tipo dado. Puedes lograr esto invocando el método renderable dentro de tu manejador de excepciones.

El cierre pasado al método renderable debe devolver una instancia de Illuminate\Http\Response, que puede generarse mediante la función auxiliar response. Laravel determinará qué tipo de excepción representa el cierre examinando la sugerencia de tipo del cierre:

use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;
 
/**
* Registra las devoluciones de llamada para el manejo de excepciones de la aplicación.
*/
public function register(): void
{
$this->renderable(function (InvalidOrderException $e, Request $request) {
return response()->view('errors.invalid-order', [], 500);
});
}

También puedes usar el método renderable para anular el comportamiento de representación para excepciones integradas de Laravel o Symfony, como NotFoundHttpException. Si el cierre dado al método renderable no devuelve un valor, se utilizará la representación predeterminada de excepciones de Laravel:

use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
/**
* Registra las devoluciones de llamada para el manejo de excepciones de la aplicación.
*/
public function register(): void
{
$this->renderable(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
}

Excepciones Reportables y Representables

En lugar de definir un comportamiento de informe y representación personalizado en el método register de tu manejador de excepciones, puedes definir los métodos report y render directamente en las excepciones de tu aplicación. Cuando estos métodos existen, se llamarán automáticamente por el framework:

<?php
 
namespace App\Exceptions;
 
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
 
class InvalidOrderException extends Exception
{
/**
* Informa de la excepción.
*/
public function report(): void
{
// ...
}
 
/**
* Representa la excepción en una respuesta HTTP.
*/
public function render(Request $request): Response
{
return response(/* ... */);
}
}

Si tu excepción extiende una excepción que ya es renderizable, como una excepción integrada de Laravel o Symfony, puedes devolver false desde el método render de la excepción para representar la respuesta HTTP predeterminada de la excepción:

/**
* Representa la excepción en una respuesta HTTP.
*/
public function render(Request $request): Response|bool
{
if (/** Determine if the exception needs custom rendering */) {
 
return response(/* ... */);
}
 
return false;
}

Si tu excepción contiene lógica de informe personalizada que solo es necesaria cuando se cumplen ciertas condiciones, es posible que necesites indicarle a Laravel que a veces informe la excepción utilizando la configuración predeterminada de manejo de excepciones. Para lograr esto, puedes devolver false desde el método report de la excepción:

/**
* Informa de la excepción.
*/
public function report(): bool
{
if (/** Determine if the exception needs custom reporting */) {
 
// ...
 
return true;
}
 
return false;
}

Nota Puedes hacer una sugerencia de tipo en cualquier dependencia requerida del método report, y se inyectarán automáticamente en el método mediante el contenedor de servicios de Laravel.

Regulación de Excepciones Informadas

Si tu aplicación informa un número muy grande de excepciones, es posible que desees limitar la cantidad de excepciones que se registran o se envían a tu servicio externo de seguimiento de errores.

Para tomar una tasa de muestra aleatoria de excepciones, puedes devolver una instancia de Lottery desde el método throttle de tu manejador de excepciones. Si la clase App\Exceptions\Handler no contiene este método, simplemente puedes agregarlo a la clase:

use Illuminate\Support\Lottery;
use Throwable;
 
/**
* Regula la entrada de excepciones.
*/
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 solo deseas muestrear instancias de una clase de excepción específica, puedes devolver una instancia de Lottery solo para esa clase:

use App\Exceptions\ApiMonitoringException;
use Illuminate\Support\Lottery;
use Throwable;
 
/**
* Regula la entrada de excepciones.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof ApiMonitoringException) {
return Lottery::odds(1, 1000);
}
}

También puedes limitar la frecuencia de las excepciones registradas o enviadas a un servicio externo de seguimiento de errores devolviendo una instancia de Limit en lugar de una instancia de Lottery. Esto es útil si deseas protegerte contra ráfagas repentinas de excepciones que inundan tus registros, por ejemplo, cuando un servicio de terceros utilizado por tu aplicación está inactivo:

use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
 
/**
* Regula la entrada de excepciones.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300);
}
}

Por defecto, los límites utilizarán la clase de excepción como clave de límite de tasa. Puedes personalizar esto especificando tu propia clave con el método by en el Limit:

use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
 
/**
* Regula la entrada de excepciones.
*/
protected function throttle(Throwable $e): mixed
{
if ($e instanceof BroadcastException) {
return Limit::perMinute(300)->by($e->getMessage());
}
}

Por supuesto, puedes devolver una combinación de instancias de 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;
 
/**
* Regula la entrada de excepciones.
*/
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, esto puede ser un error de "página no encontrada" (404), un error "no autorizado" (401) o incluso un error 500 generado por el desarrollador. Para generar dicha respuesta desde cualquier parte de tu aplicación, puedes utilizar la función abort:

abort(404);

Páginas de Error HTTP Personalizadas

Laravel facilita la visualización de páginas de error personalizadas para varios códigos de estado HTTP. Por ejemplo, para personalizar la página de error para los códigos de estado HTTP 404, crea una plantilla de vista resources/views/errors/404.blade.php. Esta vista se renderizará para todos los errores 404 generados por tu aplicación. Las vistas dentro de este directorio deben tener el nombre que coincida con el código de estado HTTP al que corresponden. La instancia de Symfony\Component\HttpKernel\Exception\HttpException generada por la función abort se pasará a la vista como una variable $exception:

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

Puedes publicar las plantillas de página de error predeterminadas de Laravel utilizando el comando Artisan vendor:publish. Una vez que se hayan publicado las plantillas, puedes personalizarlas según tus preferencias:

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

Páginas de Error HTTP de Respaldo

También puedes definir una página de error "fallback" para una serie de códigos de estado HTTP. Esta página se renderizará si no hay una página correspondiente para el código de estado HTTP específico que ocurrió. Para lograr esto, define una plantilla 4xx.blade.php y una plantilla 5xx.blade.php en el directorio resources/views/errors de tu aplicación.