Handled exceptions
Exceptions will break your application, and your users will not be able to complete what they were doing in the first place. That's how I looked at exceptions a few years ago, and it even stopped me from using them for quite some time.
But this is not always the case. For example, in a Laravel application, the validator will throw an exception when your request validation fails. This exception does not break the application; it is cached by Laravel and converted into a response to the user indicating which fields contain invalid values.
Any exception can be caught like this:
try
{
// Do something that can fail
}
catch(Throwable $t)
{
// Handle what failed
}
This is great because your application keeps running, and the user won't notice something crashes. The thing is, you probably want to know that something crashed.
An example of such a scenario here at Flare is generating AI solutions. We use an external service for this and send some information about the exception to this service. In return, we receive a solution we can show you.
Sadly, we do not always receive a response. Sometimes, the AI can't figure out an answer in the time window we specified, and thus, an exception is thrown. Flare will keep working as is since we catch those exceptions, but what if we want to keep track of these responses? Or, even worse, what if something breaks and we stop generating AI solutions? Since we catch all exceptions, we wouldn't know.
The solution is pretty simple: instead of ignoring the exception, let's report it to Flare as such (in a Laravel context):
try
{
// Contact our AI gods
}
catch(Throwable $t)
{
report($t);
}
Everything will keep working as it was, but now the exception will be stored in Flare, and we can start monitoring it there!
Since the beginning of Flare, this has already been possible, but there hasn't been a visual difference between handled and unhandled exceptions until now!
When you install the latest version of ignition and report an exception, the exception will look like this in Flare:
How did we do it
In Laravel, there's only one way to report handled exceptions: the report
method. The problem is that Laravel doesn't distinguish between manually reported exceptions and exceptions cached by the Laravel exception handler (exceptions that crash your application).
How can we determine whether the exception was handled or not? Backtracing!
If an exception is reported to Flare, it was triggered somewhere and followed a path through the application to the code that sends it to Flare. If we can somehow find that the report
method was used within that path, the exception was handled!
That path is a backtrace; do not confuse it with the exception stacktrace, which will show which path leads to the exception. The backtrace will tell us what happened after the exception was thrown.
Creating a backtrace can be done using our spatie/backtrace package:
$frames = Backtrace::create()->limit(40)->frames();
Now, we run over each frame and check the following:
- Is the frame method name equal to
report
? - Is the frame a class call and not a function call?
- Are there any frames after this frame?
In this case, it probably means we'll be in the Illuminate\Foundation\Exceptions\Handler
class; we do not check if the frame class equals this since you can define your handler.
We now start checking the next frame, which is called the Handler
class:
- Is the next frame a function call?
- Is the next frame function call also named
report
?
We'll mark the exception as handled if all these questions are confirmed for a frame within the backtrace.
PHP integration
We'll mark exceptions as handled automatically in Laravel. When using another framework or just plain PHP, you'll need to do this yourself as such:
try {
// Code that might throw an exception
} catch (Exception $exception) {
$flare->reportHandled($exception);
}