Reputation: 1744
I have been trying to implement some custom Exceptions in Laravel 5.3 with no luck.
I have created an AppException which extends the Exception class. The idea of this is that I would like to attach a user to my exceptions so I know who tripped it and can log it.
So I set up my custom exception like this:
class AppException extends Exception{
protected $userId;
public function __construct($message = '', $userId=0)
{
parent::__construct($message);
$this->userId = $userId;
}
}
Now what I would like is to use try catch's in my controller functions and then catch an exception and then rethrow the app exception so I can attach the user. Like this:
try{
<< code here >>
}
catch(Exception $e){
throw new AppException($e->getMessage, $request->user()->id);
}
What I am finding is that I am unable to get a good trace stack because the line I am logging from my exception is the line from the rethrow in the catch, not from the actual exception.
What would be the right way to set this up? I am trying to do this in a way where I can utilize the built in Handler.php file that comes with Laravel instead of having to put in log code in every try catch.
Thanks!
Upvotes: 2
Views: 6098
Reputation: 9582
Try attaching the actual exception to the thrown exception, for example like this:
class AppException extends \Exception
{
/**
* @var int
*/
private $userId;
/**
* @param \Exception $exception
* @param int $userId
*
* @return self
*/
public static function fromExceptionAndUserId(\Exception $exception, $userId)
{
$instance = new self(
$exception->getMessage(),
0,
$exception
);
$instance->userId = $userId;
return $instance;
}
/**
* @return null|int
*/
public function userId()
{
return $this->userId;
}
}
Then in your controller:
try {
// ...
} catch (\Exception $exception) {
throw AppException::fromExceptionAndUserId(
$exception,
$request->user()->id
);
}
The idea is to not only use the actual exception message, but to also attach the actual exception as $previous
exception.
Using a named constructor gives you the advantage that you can control how the exception message of AppException
is constructed (while also attaching the user identifier), as well as still being able to use the original AppException
constructor. It gives you the freedom to add more named constructors for different use cases, too.
For reference, see
Upvotes: 0
Reputation: 36924
The Exception
class has a previous
argument that you can use to retrieve all the previous Exception.
class AppException extends Exception
{
private $userId;
public function __construct($userId, $message = '', $code = 0, Exception $previous = null)
{
$this->userId = $userId;
parent::__construct($message, $code, $previous);
}
public static function fromUserId($userId, Exception $previous = null)
{
return new static($userId, sprintf('Exception thrown from `%s` userid', $userId), 0, $previous);
}
public function getUserId()
{
return $this->userId;
}
}
Or just simply
class AppException extends Exception
{
public static function fromUserId($userId, Exception $previous = null)
{
return new static(sprintf('Exception thrown from `%s` userid', $userId), 0, $previous);
}
}
After you catch AppException
you can iterate all the exception like this:
do {
printf("%s:%d %s (%d) [%s]\n", $e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), get_class($e));
} while($e = $e->getPrevious());
Example:
try {
try{
throw new RuntimeException('Something bad happened.');
}
catch(Exception $e){
throw AppException::fromUserId(123, $e);
}
} catch(AppException $e) {
do {
printf("%s:%d %s (%d) [%s]\n", $e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), get_class($e));
} while($e = $e->getPrevious());
}
Upvotes: 8