1. Go to this page and download the library: Download nikolaynesov/laravel-serene library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Nikolaynesov\LaravelSerene\Facades\Serene;
use Throwable;
class Handler extends ExceptionHandler
{
public function register(): void
{
$this->reportable(function (Throwable $e) {
Serene::report($e, [
'user_id' => auth()->id(),
]);
});
}
}
use Nikolaynesov\LaravelSerene\Facades\Serene;
try {
// Your code that might throw an exception
$user = User::findOrFail($id);
} catch (\Exception $e) {
Serene::report($e);
// Handle the error gracefully
return response()->json(['error' => 'User not found'], 404);
}
use Nikolaynesov\LaravelSerene\Contracts\GroupableException;
class PaymentException extends \Exception implements GroupableException
{
public function getErrorGroup(): string
{
return 'payment-processing-error';
}
}
// All PaymentExceptions are automatically grouped together
throw new PaymentException("Payment failed for user {$userId}");
throw new PaymentException("Payment declined for order {$orderId}");
// Both grouped as 'payment-processing-error' regardless of message
class ApiException extends \Exception implements GroupableException
{
public function __construct(
string $message,
private string $endpoint
) {
parent::__construct($message);
}
public function getErrorGroup(): string
{
return "api:{$this->endpoint}";
}
}
// Automatically groups by endpoint
throw new ApiException('Timeout', 'stripe/charges');
throw new ApiException('Invalid request', 'stripe/charges');
// Both grouped as 'api:stripe/charges'
// Group by API endpoint
Serene::report($exception, ['user_id' => $user->id], 'api:payment:stripe');
// Group by specific operation
Serene::report($exception, ['order_id' => $order->id], 'order-processing');
// Auto-grouped by class and message
throw new RuntimeException('Database connection failed');
// Key: auto:runtimeexception:abc123
// config/serene.php
'group_by_key' => false,
[
'affected_users' => [1, 5, 12, 43], // Array of user IDs (max 1000)
'affected_user_count' => 4, // Total affected users tracked
'user_tracking_capped' => false, // True if hit max_tracked_users limit
'occurrences' => 15, // Total times error occurred
'throttled' => 14, // Times error was throttled
'reported_at' => '2025-12-05 18:30:00', // When reported
'key' => 'auto:runtimeexception:abc123', // Error key
// ... your custom context
]
// config/serene.php
'debug' => true,
// When an error is reported (info level)
[Serene] auto:runtimeexception:abc123 reported
{
"affected_users": [1, 5, 12],
"count": 3,
"occurrences": 15,
"throttled": 14
}
// When an error is throttled (debug level)
[Serene] auto:runtimeexception:abc123 throttled
{
"occurrences": 16,
"throttled": 15
}
namespace App\ErrorReporters;
use Nikolaynesov\LaravelSerene\Contracts\ErrorReporter;
use Throwable;
class SentryReporter implements ErrorReporter
{
public function report(Throwable $exception, array $context = []): void
{
\Sentry\captureException($exception, [
'extra' => $context,
]);
}
}