zufarmarwah / laravel-performance-guard
Production-safe performance monitoring for Laravel - detect N+1 queries, slow queries, and memory issues
Package info
github.com/zooster90/laravel-performance-guard
pkg:composer/zufarmarwah/laravel-performance-guard
Requires
- php: ^8.1
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- nunomaduro/collision: ^7.0|^8.0
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
- phpstan/phpstan: ^1.0
README
Laravel Performance Guard
Production-safe performance monitoring for Laravel. Catch N+1 queries, slow queries, and performance issues before your users do.
Why Performance Guard?
Most performance issues in Laravel apps go unnoticed until users complain. N+1 queries silently multiply, slow queries stack up, and memory usage creeps higher with every deployment.
Performance Guard sits in your middleware stack, monitors every request, and gives you a clear picture of what's happening -- with zero changes to your application code.
- Drop-in middleware -- no code changes required in your controllers or models
- Production-safe -- configurable sampling, async storage, and instant kill switch
- Actionable insights -- tells you which relationship to eager load, not just "you have duplicates"
- Privacy-first -- automatically redacts passwords, tokens, and sensitive data from recorded queries
Features
- N+1 Query Detection -- detects duplicate query patterns, ignores framework tables (cache, sessions), and distinguishes eager-loading vs aggregate query suggestions
- Slow Query Monitoring -- identifies slow queries with optimization suggestions
- Request Detail View -- click any request to see all queries, duplicate patterns, and fix suggestions
- Developer Overlay -- floating pill on your website showing real-time queries, duration, and N+1 issues while you code (like Debugbar, but lighter)
- Web Vitals -- capture real user metrics (LCP, CLS, INP) from the browser and display them alongside server metrics
- CI/CD Performance Budgets --
performance-guard:checkcommand fails your build if routes exceed duration/query thresholds or have N+1 issues - Performance Grading -- grades every request A-F based on response time
- Built-in Dashboard -- dark-themed dashboard with auto-refresh to visualize performance metrics
- Impact Scoring -- routes sorted by impact (requests x avg duration) to prioritize what matters most
- CSV Export -- export route performance data for reporting and analysis
- Notifications -- alerts via Slack, Email, or Telegram when issues are detected
- Queue Support -- stores metrics asynchronously to avoid impacting request performance
- Privacy First -- automatically redacts sensitive data from recorded queries
- Sampling -- configurable sampling rate for high-traffic production environments
- Auto Cleanup -- artisan command to purge old records with configurable retention
- High Memory Detection -- alerts when requests exceed memory thresholds
- Route Aggregation -- per-route performance breakdown with controller names and impact scoring
- Smart Filtering -- auto-excludes redirect responses, dashboard self-monitoring, and framework table queries
- Trend Comparison -- period-over-period comparison showing improvement or regression
- Artisan Status Command -- quick terminal overview of performance health
- Octane Compatible -- safe for long-running processes with proper state isolation
Screenshots
Dashboard Overview
Stats cards with trend indicators, grade distribution, and recent requests table.
Route Performance
Per-route aggregation with controller names, impact scoring, N+1/slow query issue flags, and CSV export.
Request Detail -- N+1 Detection
Click any request to see the full query breakdown. Duplicate query patterns are grouped with count badges, total duration, and eager-loading fix suggestions.
Request Detail -- Controller & Source Tracing
Each request shows the controller, action, user ID, IP, and source file:line for every query -- making it easy to trace exactly where queries originate.
Request Detail -- Clean Request
Requests with no issues show a clean view with zero queries and status code.
Requirements
- PHP 8.1+
- Laravel 10, 11, or 12
Database Compatibility
Works with any database supported by Laravel:
| Database | Status |
|---|---|
| MySQL / MariaDB | Fully tested |
| PostgreSQL | Fully compatible |
| SQLite | Fully compatible (used in test suite) |
| SQL Server | Compatible via query builder |
Installation
Note: The Composer package name is
zufarmarwah/laravel-performance-guard.
composer require zufarmarwah/laravel-performance-guard
Then run the install command (publishes config + runs migrations in one step):
php artisan performance-guard:install
Or install manually
php artisan vendor:publish --tag=performance-guard-config php artisan migrate
Quick Start
Laravel 11+ (bootstrap/app.php)
To monitor all web requests, append the middleware globally:
->withMiddleware(function (Middleware $middleware) { $middleware->appendToGroup('web', \Zufarmarwah\PerformanceGuard\Middleware\PerformanceMonitoringMiddleware::class); })
Or apply it to specific routes only:
Route::middleware(['performance-guard'])->group(function () { Route::get('/users', [UserController::class, 'index']); Route::get('/posts', [PostController::class, 'index']); });
Laravel 10 (app/Http/Kernel.php)
Add to a middleware group or use the alias on specific routes:
// In Kernel.php -- monitor all web requests protected $middlewareGroups = [ 'web' => [ // ...existing middleware \Zufarmarwah\PerformanceGuard\Middleware\PerformanceMonitoringMiddleware::class, ], ];
// Or on specific routes Route::middleware(['performance-guard'])->group(function () { // your routes });
Visit /performance-guard in your browser. That's it.
Tip: By default, data is stored synchronously so you see results immediately. For production, set
PERFORMANCE_GUARD_ASYNC=truein your.envto store via queue.
Configuration
After publishing, edit config/performance-guard.php:
return [ // Toggle monitoring on/off 'enabled' => env('PERFORMANCE_GUARD_ENABLED', true), // Monitor a percentage of requests (0.0 to 1.0) 'sampling_rate' => env('PERFORMANCE_GUARD_SAMPLING_RATE', 1.0), 'thresholds' => [ 'n_plus_one' => 10, // Duplicate queries to trigger N+1 alert 'slow_query_ms' => 300, // Query duration threshold (ms) 'slow_request_ms' => 1000, // Request duration threshold (ms) 'memory_mb' => 128, // Memory usage threshold (MB) 'query_count' => 50, // Total query count warning ], // Performance grading scale (duration in ms) 'grading' => [ 'A' => 200, // <= 200ms 'B' => 500, // <= 500ms 'C' => 1000, // <= 1000ms 'D' => 3000, // <= 3000ms // Everything above = F ], ];
See the full config file for all options including dashboard, notifications, storage, and privacy settings.
Dashboard
The built-in dashboard is available at /performance-guard and includes four views:
| View | What it shows |
|---|---|
| Overview | Total requests, avg duration, query counts, memory, grades with trend indicators (auto-refreshes every 30s) |
| N+1 Issues | All requests where duplicate query patterns were detected -- click any row to see which queries to fix |
| Slow Queries | All requests containing slow database queries -- click to see individual slow queries |
| Routes | Per-route aggregation with controller name, impact score, and CSV export |
| Request Detail | Full query breakdown with duplicate grouping, eager-load suggestions, and source file locations |
Period filtering is available for 1 hour, 24 hours, 7 days, or 30 days.
Click any request row to drill into the Request Detail view, which shows:
- All SQL queries executed during the request
- Duplicate query patterns grouped with fix suggestions (e.g., "add
->with('posts')to eager load") - Slow queries highlighted with duration
- Source file and line number for each query
Dashboard Access Control
By default, the dashboard requires authorization. Define the gate in your AuthServiceProvider:
use Illuminate\Support\Facades\Gate; Gate::define('viewPerformanceGuard', function ($user) { return in_array($user->email, [ 'admin@example.com', ]); });
You can also restrict by IP or email whitelist:
'dashboard' => [ 'auth' => true, 'allowed_ips' => ['127.0.0.1', '10.0.0.1'], 'allowed_emails' => ['admin@example.com'], ],
Set auth to false for open access (not recommended in production).
Developer Overlay
A floating pill in the corner of your website that shows real-time performance metrics while you develop -- no tab switching needed.
PERFORMANCE_GUARD_OVERLAY=true
Then add the overlay middleware after the monitoring middleware:
// Laravel 11+ (bootstrap/app.php) ->withMiddleware(function (Middleware $middleware) { $middleware->appendToGroup('web', [ \Zufarmarwah\PerformanceGuard\Middleware\PerformanceMonitoringMiddleware::class, \Zufarmarwah\PerformanceGuard\Middleware\PerformanceOverlayMiddleware::class, ]); })
The overlay shows:
- Performance grade badge (A-F)
- Query count and response duration
- N+1 and slow query warnings
- Click to expand: full query list, suggestions, and link to dashboard
Web Vitals
Enable browser-side metrics (LCP, CLS, INP) alongside the overlay:
PERFORMANCE_GUARD_WEB_VITALS=true
Web Vitals are captured via the Performance Observer API and sent to the dashboard automatically. No external dependencies required.
CI/CD Performance Budgets
Stop slow code from reaching production. The performance-guard:check command validates recorded performance data against budgets and returns exit code 1 on failure.
# Check all routes against default budgets php artisan performance-guard:check # Custom budgets php artisan performance-guard:check --max-duration=500 --max-queries=30 # Check a specific route php artisan performance-guard:check --route=/api/bookings # Fail if any N+1 issues php artisan performance-guard:check --fail-on-n-plus-one
GitHub Actions Example
- name: Run test suite run: php artisan test - name: Check performance budgets run: php artisan performance-guard:check --period=1h --fail-on-n-plus-one
Configure defaults in config/performance-guard.php:
'ci' => [ 'max_duration_ms' => 500, 'max_queries' => 30, 'fail_on_n_plus_one' => true, ],
API Endpoints
All dashboard data is available as JSON:
| Endpoint | Description |
|---|---|
GET /performance-guard/api |
Overview stats with grade distribution |
GET /performance-guard/api/{uuid} |
Single record with all recorded queries (JSON) |
GET /performance-guard/request/{uuid} |
Request detail page with query breakdown (HTML) |
GET /performance-guard/n-plus-one |
N+1 issues (paginated) |
GET /performance-guard/slow-queries |
Slow query records (paginated) |
GET /performance-guard/routes |
Route-level aggregated stats (paginated) |
GET /performance-guard/routes/export |
Download route data as CSV |
POST /performance-guard/api/vitals |
Store browser Web Vitals (LCP, CLS, INP) |
GET /performance-guard/api/vitals |
Aggregate Web Vitals per URL |
All endpoints accept a ?period= parameter (1h, 24h, 7d, 30d).
Notifications
Get alerted when performance issues occur.
Slack
PERFORMANCE_GUARD_NOTIFICATIONS=true PERFORMANCE_GUARD_SLACK_WEBHOOK=https://hooks.slack.com/services/...
Telegram
PERFORMANCE_GUARD_NOTIFICATIONS=true PERFORMANCE_GUARD_TELEGRAM_TOKEN=123456:ABC-DEF... PERFORMANCE_GUARD_TELEGRAM_CHAT_ID=-1001234567890
To get your bot token, message @BotFather. To get your chat ID, add the bot to a group and check https://api.telegram.org/bot<TOKEN>/getUpdates.
'notifications' => [ 'enabled' => true, 'channels' => [ 'email' => [ 'enabled' => true, 'recipients' => ['ops@yourcompany.com'], ], ], ],
Alert Types
| Alert | Trigger |
|---|---|
| N+1 Queries | Duplicate query pattern detected |
| Slow Queries | Query exceeds duration threshold |
| Slow Request | Request exceeds duration threshold |
| High Memory | Memory usage exceeds threshold |
| Grade F | Request received the lowest grade |
A cooldown period (default: 15 minutes) prevents alert spam for the same issue.
Artisan Commands
Status
Get a performance overview in your terminal:
php artisan performance-guard:status
# Different time periods
php artisan performance-guard:status --period=1h
php artisan performance-guard:status --period=7d
php artisan performance-guard:status --period=30d
Shows requests, avg duration, queries, memory, N+1 count, grade distribution, and the 5 slowest routes -- all with trend indicators.
Check (CI/CD)
Validate performance against budgets:
php artisan performance-guard:check php artisan performance-guard:check --max-duration=500 --max-queries=30 --fail-on-n-plus-one php artisan performance-guard:check --route=/api/bookings --period=1h
Returns exit code 0 (pass) or 1 (fail). See CI/CD Performance Budgets for setup.
Cleanup
Remove old records to keep your database lean:
# Use configured retention (default: 30 days) php artisan performance-guard:cleanup # Custom retention php artisan performance-guard:cleanup --days=7 # Skip confirmation php artisan performance-guard:cleanup --force
Scheduling Cleanup
Laravel 11+ (bootstrap/app.php):
->withSchedule(function (\Illuminate\Console\Scheduling\Schedule $schedule) { $schedule->command('performance-guard:cleanup --force')->daily(); })
Laravel 10 (app/Console/Kernel.php):
$schedule->command('performance-guard:cleanup --force')->daily();
Production Deployment
Recommended .env settings:
PERFORMANCE_GUARD_ENABLED=true PERFORMANCE_GUARD_SAMPLING_RATE=0.1 PERFORMANCE_GUARD_ASYNC=true PERFORMANCE_GUARD_QUEUE=performance PERFORMANCE_GUARD_RETENTION_DAYS=14
| Setting | Value | Why |
|---|---|---|
SAMPLING_RATE |
0.1 |
Monitor 10% of requests -- reduces overhead while catching patterns |
ASYNC |
true |
Recording never blocks the HTTP response |
QUEUE |
performance |
Dedicated queue prevents competition with business jobs |
RETENTION_DAYS |
14 |
Keeps database size manageable |
Emergency Kill Switch
Disable instantly without a deploy:
PERFORMANCE_GUARD_ENABLED=false
Or at runtime:
use Zufarmarwah\PerformanceGuard\Facades\PerformanceGuard; PerformanceGuard::disable();
Facade
use Zufarmarwah\PerformanceGuard\Facades\PerformanceGuard; // Get stats for a period $stats = PerformanceGuard::getStats('24h'); // Get grade distribution $grades = PerformanceGuard::getGradeDistribution('7d'); // Toggle monitoring PerformanceGuard::enable(); PerformanceGuard::disable(); // Check status if (PerformanceGuard::isEnabled()) { // ... }
How It Works
Request --> Middleware --> QueryListener (DB::listen)
|
Response sent
|
NPlusOneAnalyzer (detects duplicates)
SlowQueryAnalyzer (finds slow queries)
PerformanceScorer (grades A-F)
|
StorePerformanceRecordJob (queued)
|
NotificationDispatcher (if configured)
- The
performance-guardmiddleware wraps each request - A
QueryListenercaptures every SQL query viaDB::listen() - After the response, analyzers process the captured data
- Results are stored asynchronously via a queued job
- Notifications are dispatched if thresholds are exceeded
- Sensitive data is automatically redacted before storage
Octane Support
Performance Guard is compatible with Laravel Octane. The QueryListener state is properly reset between requests to prevent data leaking across long-lived worker processes.
Testing
composer test
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Write tests for your changes
- Ensure all tests pass (
composer test) - Submit a pull request
License
MIT License. See LICENSE for details.




