spatie/laravel-ray
Send Laravel debug output to Ray, Spatie’s desktop debugging app. Use a consistent API to inspect variables, arrays, HTML, queries and more, measure performance, and pause execution. Works across Laravel/PHP with Ray’s rich UI.
Installation:
composer require spatie/laravel-ray
php artisan ray:install
This publishes the config file and registers the service provider.
Configure Ray:
Edit .env to include:
RAY_PROJECT=your_project_name
RAY_APP_ID=your_app_id # Get this from the Ray app
First Debug Output:
Use ray() helper in your code:
ray($user); // Dumps the $user variable to Ray
Or use the @ray directive in Blade:
@ray($data)
Launch Ray: Open the Ray desktop app and connect to your project.
Replace dd() or dump() calls in app/Http/Controllers/UserController.php:
public function show(User $user)
{
ray($user->toArray()); // Replace dd($user)
return view('user.show', compact('user'));
}
Now inspect the output in Ray with rich formatting (no more scrolling through CLI logs).
Replacing Debugging Tools:
dd(), dump(), or Log::debug() with ray().// Before
Log::debug('User data', ['user' => $user->toArray()]);
// After
ray($user->toArray());
Query Debugging:
Enable query logging in config/ray.php:
'watchers' => [
\Spatie\Ray\Watchers\QueryWatcher::class,
],
Now all queries appear in Ray with execution time and SQL.
Blade Debugging:
Use @ray directives in views:
@ray($post->comments)
@ray($user->roles, 'User Roles')
Performance Measurement:
Use ray()->measure() to time blocks:
ray()->measure('Process user', function () {
$user->load('orders');
});
Conditional Debugging:
Wrap ray() calls in debug mode checks:
if (app()->environment('local')) {
ray($data);
}
Custom Context: Add context to logs for better organization:
ray($user, context: ['action' => 'profile_update']);
Exception Handling:
Use ray() in App\Exceptions\Handler:
public function report(Throwable $exception)
{
ray($exception, context: ['url' => request()->url()]);
parent::report($exception);
}
Artisan Commands:
Debug commands with ray():
public function handle()
{
ray('Command executed', context: ['command' => 'user:export']);
}
Queue Jobs:
Debug jobs by adding ray() to handle():
public function handle()
{
ray('Job payload', $this->data);
}
Event Listeners: Log events with context:
public function handle(OrderPlaced $event)
{
ray($event->order, context: ['event' => 'order_placed']);
}
Middleware: Inspect requests/responses:
public function handle($request, Closure $next)
{
$response = $next($request);
ray($request->all(), context: ['middleware' => 'LogRequest']);
return $response;
}
Custom Watchers:
Extend \Spatie\Ray\Watchers\Watcher to log custom data:
class CacheWatcher extends Watcher
{
public function __construct()
{
$this->name = 'cache';
}
public function log($key, $value)
{
$this->ray->log($value, context: ['cache_key' => $key]);
}
}
Register in config/ray.php:
'watchers' => [
\App\Watchers\CacheWatcher::class,
],
Ray in Tests:
Use ray() in tests for debugging:
public function test_user_creation()
{
$user = User::create([...]);
ray($user); // Debug in Ray during test runs
$this->assertTrue($user->exists);
}
Cleanup:
Remove ray() calls before production:
php artisan ray:clean
Performance Overhead:
ray() adds network overhead. Avoid in production or high-traffic endpoints.if (app()->environment('local')) to gate usage.Ray App Connection:
.env for RAY_APP_ID and RAY_PROJECT.Query Watcher:
QueryWatcher logs all queries, which can be verbose. Use conditionally:
if (app()->environment('local')) {
\Spatie\Ray\Ray::enableQueryWatcher();
}
Blade Directives:
@ray directives are parsed at runtime. Avoid in loops with large datasets:
@foreach ($largeCollection as $item)
@ray($item) <!-- May slow down rendering -->
@endforeach
Context Limits:
->toArray() or json_encode().PHP 8.5+ Deprecations:
Missing Ray Output:
RAY_APP_ID and RAY_PROJECT in .env.Spatie\Ray\RayServiceProvider is registered in config/app.php.Query Watcher Not Working:
QueryWatcher is enabled in config/ray.php.Blade @ray Not Rendering:
php artisan view:clear
Large Payloads:
ray()->log() with explicit context to avoid truncation:
ray()->log('Large data', ['chunk' => array_chunk($data, 1000)], context: ['type' => 'chunked']);
Artisan Commands:
ray() doesn’t appear in Artisan commands, ensure the Ray service provider is loaded:
$this->app->register(\Spatie\Ray\RayServiceProvider::class);
Custom Project Name:
Set in config/ray.php:
'project_name' => env('RAY_PROJECT', 'my_app'),
Disable Ray:
Temporarily disable in config/ray.php:
'enabled' => env('RAY_ENABLED', false),
Custom Port:
Ray uses a local server. If port 8080 is blocked, configure in config/ray.php:
'port' => env('RAY_PORT', 8080),
Ignored Exceptions: Exclude specific exceptions from Ray logs:
'ignored_exceptions' => [
\App\Exceptions\MaintenanceModeException::class,
],
Custom Ray Client:
Override the default Ray client in AppServiceProvider:
public function boot()
{
$this->app->singleton(\Spatie\Ray\Ray::class, function () {
return new \Spatie\Ray\Ray(
new \Spatie\Ray\RayClient(
env('RAY_APP_ID'),
env('RAY_PROJECT'),
env('RAY_PORT', 8080)
)
);
});
}
Ray Middleware: Create middleware to log requests/responses:
class RayLoggingMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
ray($request->all(), context: ['url' => $request->url()]);
ray($response->
How can I help you explore Laravel packages today?