mzshovon / laravel-trycatcher
A lightweight exception guard for Laravel to simplify try-catch handling with logging and policies.
Installs: 10
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/mzshovon/laravel-trycatcher
Requires
- php: >=8.0
Requires (Dev)
- orchestra/testbench: 8.x-dev
- phpunit/phpunit: ^9.0
This package is auto-updated.
Last update: 2025-12-24 08:13:51 UTC
README
A lightweight exception guard for Laravel to simplify try/catch handling with consistent logging, safe responses, and attribute-driven policies.
Why this package?
- Eliminate boilerplate: Replace repetitive try/catch blocks with concise guards
- Consistent behavior: Centralized policies for logging and responses
- Flexible usage: Use a Facade, helper, or trait with PHP 8 attributes
- Production-safe: Return safe messages in production without leaking traces
- Persistent logs: Store errors in
error_logstable and Laravel logs
Requirements
- PHP >= 8.0
- Laravel 9+ (auto-discovery enabled)
Installation
composer require mzshovon/laravel-trycatcher
The package supports Laravel package discovery. No manual provider registration is required.
Publish configuration and migrations:
php artisan vendor:publish --provider="Mzshovon\LaravelTryCatcher\ExceptionGuardServiceProvider" --tag=config php artisan vendor:publish --provider="Mzshovon\LaravelTryCatcher\ExceptionGuardServiceProvider" --tag=migrations php artisan migrate
Configuration
Published to config/exception-guard.php:
return [ 'default_policy' => \Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy::THROW, 'prod_policy' => \Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy::PROD_SAFE, 'log_channel' => env('EXCEPTION_GUARD_LOG_CHANNEL', 'stack'), 'integrations' => [ 'sentry' => env('SENTRY_DSN') ? true : false, 'slack' => env('EXCEPTION_GUARD_SLACK_WEBHOOK') ? true : false, ], 'safe_prod_guard' => 1, ];
- default_policy: Used when no explicit policy/attribute is found
- prod_policy: Recommended production-safe policy
- safe_prod_guard: When enabled and
app.env=production, responses avoid traces regardless of requested policy
Policies
Available values in \Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy:
THROW: Rethrow to Laravel’s handlerLOG: Persist to DB and Laravel log (no stack trace in DB)LOG_WITH_TRACE: Persist to DB and Laravel log with stack traceEXCEPTION_ONLY: Return structured error payload (includes trace only whenapp.debug=true)PROD_SAFE: Log minimally and return a safe generic messageLOG_AND_THROW: Log then rethrow
Note: When safe_prod_guard is enabled and the app is in production, responses are converted to a safe generic payload even if a policy would otherwise include details.
Usage
1) Trait + Attribute
Annotate methods with a policy and call them normally; exceptions will be intercepted and handled.
use Mzshovon\LaravelTryCatcher\Traits\Guardable; use Mzshovon\LaravelTryCatcher\Attributes\ExceptionPolicyAttr; use Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy; class PaymentService { use Guardable; #[ExceptionPolicyAttr(ExceptionPolicy::LOG_AND_THROW, options: ['context' => ['service' => 'payment']])] protected function charge(array $payload) { // ... risky operation ... return 'ok'; } public function runCharge(array $payload) { // Will honor the attribute policy return $this->runGuardedMethod('charge', [$payload]); } }
Protected and parent methods
runGuardedMethod supports calling protected/private methods (including those declared on a parent class). The trait uses reflection and will set accessibility when required.
use Mzshovon\LaravelTryCatcher\Traits\Guardable; use Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy; abstract class BaseService { use Guardable; // Declared in parent, not public #[\Mzshovon\LaravelTryCatcher\Attributes\ExceptionPolicyAttr(ExceptionPolicy::LOG)] protected function persist(array $data) { // ... risky work ... return 'saved'; } } class UserService extends BaseService { public function save(array $data) { // Calls protected parent method with args; attribute is honored return $this->runGuardedMethod('persist', [$data]); } }
2) Facade
use Guard; // alias of \Mzshovon\LaravelTryCatcher\Facades\Guard use Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy; $result = Guard::run(function () { // risky work return doSomething(); }, ExceptionPolicy::LOG_WITH_TRACE, [ 'context' => ['user_id' => auth()->id()], 'status' => 500, ]);
3) Helper
use Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy; $response = guarded(function () { return doAnotherThing(); }, ExceptionPolicy::PROD_SAFE, [ 'message' => 'Please try again later', 'status' => 422, ]);
You can also pass method callables. If no policy is provided, the helper attempts to discover an ExceptionPolicyAttr attribute on that method and falls back to default_policy otherwise.
Alternatively, when calling methods directly on an object using Guardable, the magic __call will intercept and apply the attribute policy automatically.
Passing arguments and runtime objects
- Attributes in PHP cannot accept runtime objects; they only allow constant expressions. If you need to work with runtime object references (e.g., a repository or DTO), pass them as method arguments and invoke via
runGuardedMethod:
use Mzshovon\LaravelTryCatcher\Policies\ExceptionPolicy; class ReportService { use \Mzshovon\LaravelTryCatcher\Traits\Guardable; // No attribute here; we want dynamic control protected function generate($reportGenerator, array $params) { return $reportGenerator->build($params); } public function safeGenerate($reportGenerator, array $params) { return $this->runGuardedMethod( 'generate', [$reportGenerator, $params], ExceptionPolicy::PROD_SAFE, [ 'context' => ['type' => 'report'], 'message' => 'Report generation failed. Please try again later.', 'status' => 503, ] ); } }
- Alternatively, wrap your logic in a closure and use the
guarded()helper or theGuardfacade when you need full control over captured variables.
Options
Pass via the third parameter to guards/handlers or via the attribute’s options:
context(array): Extra data included in logslevel(string): Log level persisted to DB (defaulterror)status(int): HTTP status for JSON responsesmessage(string): Safe message for production responses
Database Logging
Errors are persisted in error_logs with the following schema:
$table->id(); $table->string('level')->nullable(); $table->string('message'); $table->text('trace')->nullable(); $table->json('context')->nullable(); $table->timestamps();
Model: \Mzshovon\LaravelTryCatcher\Models\ErrorLog (casts context to array).
On any failure to persist, the package falls back to Laravel file logging to avoid losing error details.
JSON Response Shape
Non-throwing policies return a JSON response similar to:
{
"error": true,
"message": "Something went wrong.",
"trace": [] // included only when applicable and app.debug=true
}
In production with safe_prod_guard enabled, responses omit traces and use the provided message option or a generic message.
Testing
- Use policies like
EXCEPTION_ONLYto assert structured error payloads - Seed a failure and assert a row exists in
error_logs - In production-like tests, enable
safe_prod_guardand assert generic responses
License
Licensed under the MIT License. See LICENSE.