halilcosdu/laravel-slower
Detect and log slow Laravel database queries, then get AI-powered suggestions for indexes and query improvements. Configurable thresholds, can run with or without AI, and supports Laravel 10–13 on PHP 8.2+.
Installation (unchanged):
composer require halilcosdu/laravel-slower
php artisan vendor:publish --tag="slower-config"
php artisan vendor:publish --tag="slower-migrations"
php artisan migrate
Configure .env (additions highlighted):
SLOWER_ENABLED=true
SLOWER_THRESHOLD=10000 # Log queries slower than 10ms
OPENAI_API_KEY=your_openai_key
OPENAI_ORGANIZATION=your_org_id
# New v3.2.0 controls
SLOWER_CAPTURE_SAMPLE_RATE=1.0 # 0.0–1.0 (1.0 = always capture)
SLOWER_CAPTURE_MAX_PER_EXECUTION=100
SLOWER_ANALYZE_QUEUE=slow_analysis # Leave unset for sync
Enable Query Logging (updated for origin tracking):
public function boot()
{
if (config('slower.enabled')) {
\DB::enableQueryLog();
\DB::listen(function ($query) {
if (\DB::getQueryLog() && \DB::getQueryLog()[0]['time'] > config('slower.threshold')) {
\HalilCosdu\Slower\Facades\Slower::capture($query);
}
});
}
}
php artisan slower:events --grouped
Capture with Context:
Use capture() instead of log() to include origin metadata:
\HalilCosdu\Slower\Facades\Slower::capture($query);
Analyze Asynchronously (new):
// Dispatch analysis as a background job
\HalilCosdu\Slower\Facades\Slower::analyze($log)->onQueue('slow_analysis');
Review Grouped Events:
# CLI grouped view (new)
php artisan slower:events --grouped
# Programmatic access
$grouped = \HalilCosdu\Slower\Facades\Slower::groupedEvents();
Leverage Events (new):
// Listen for new slow queries
event(new \HalilCosdu\Slower\Events\SlowQueryCaptured($log));
// Listen for first occurrence of a fingerprint
event(new \HalilCosdu\Slower\Events\SlowQueryFirstSeen($fingerprint));
Sampled Capture (new): Reduce overhead in high-traffic apps:
SLOWER_CAPTURE_SAMPLE_RATE=0.1 # Capture 10% of slow queries
Queue Analysis (new): Configure a dedicated queue for analysis:
SLOWER_ANALYZE_QUEUE=slow_analysis
Then run workers:
php artisan queue:work --queue=slow_analysis
Origin Filtering: Filter events by origin (e.g., API routes):
$apiEvents = \HalilCosdu\Slower\Models\SlowLog::where('origin_route', 'like', '/api%')->get();
Backfill Fingerprints (one-time):
php artisan slower:fingerprint # Chunked, idempotent
Fingerprint Normalization:
IN (...) or literals may create duplicate fingerprints. Use:
SLOWER_FINGERPRINT_ESCAPE_IN_LIST=true # Default: false
Origin Overhead:
\HalilCosdu\Slower\Facades\Slower::capture($query, origin: false);
AI Payload Privacy:
SLOWER_AI_PAYLOAD_INCLUDE_RAW_SQL=true
SLOWER_AI_PAYLOAD_INCLUDE_BINDINGS=true
php artisan slower:test-redactor
Queued Analysis:
shouldQueue() to guard:
$job = \HalilCosdu\Slower\Facades\Slower::analyze($log);
if ($job && $job->shouldQueue()) {
$job->onQueue('slow_analysis')->dispatch();
}
Verify Fingerprints: Check normalization with:
php artisan slower:fingerprint --dry-run
Inspect Events: Dump raw event data:
$event = \HalilCosdu\Slower\Models\SlowLog::find(1);
dd($event->toArray());
Queue Issues: Monitor stuck jobs:
php artisan queue:failed-table
Circuit Breaker: Storage failures trigger a 60s break. Check logs for:
[Slower] Circuit breaker armed: storage failure
Custom Fingerprint Normalizer:
Extend \HalilCosdu\Slower\Services\FingerprintNormalizer to handle edge cases.
Origin Resolvers: Add custom origin resolvers (e.g., for CLI commands):
\HalilCosdu\Slower\Services\OriginResolver::extend('cli', function () {
return 'cli:' . $_SERVER['argv'][0];
});
Payload Redactors:
Implement \HalilCosdu\Slower\Contracts\PayloadRedactor to sanitize sensitive data:
class MyRedactor implements PayloadRedactor {
public function redact(array $payload): array {
$payload['sql'] = str_replace('password', '[REDACTED]', $payload['sql']);
return $payload;
}
}
Register in config/slower.php:
'ai_payload' => [
'redactor' => \App\Services\MyRedactor::class,
],
Event Listeners: React to slow queries in real-time:
\Event::listen(\HalilCosdu\Slower\Events\SlowQueryCaptured::class, function ($event) {
if ($event->log->time > 5000) {
\Log::alert("Critical slow query: {$event->log->fingerprint}");
}
});
Grouped View Customization: Override the grouped query builder:
\HalilCosdu\Slower\Services\GroupedView::macro('customFilter', function () {
return $this->where('time', '>', 20000);
});
Usage:
$grouped = \HalilCosdu\Slower\Facades\Slower::groupedEvents()->customFilter();
How can I help you explore Laravel packages today?