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+.
The first phase of the 2026 roadmap: make Slower something you confidently leave on in production, and make every capture know what it is (a fingerprint) and where it came from (its origin). No breaking changes — additive migration, existing config/API untouched, synchronous analysis still the default.
IN (...) normalized in a single lexer-style pass). The dashboard gains an Events | Grouped toggle — one row per query shape per connection, with occurrence count, avg/max duration and last-seen, drilling down to the underlying events. php artisan slower:fingerprint backfills pre-3.2 rows (chunked, idempotent).Controller@action, queue job class, or artisan command — plus the first file:line of application code (taken only for threshold-exceeding queries, DEBUG_BACKTRACE_IGNORE_ARGS). Shown on the detail page and fed to the AI prompt. The authenticated user id is opt-in and never forwarded to the LLM.capture.sample_rate, capture.max_per_execution, a 60s circuit breaker when storage itself fails, and a hardened self-capture guard.PayloadRedactor covers every outbound path (raw SQL, bindings, and the EXPLAIN plan); a misconfigured redactor throws instead of silently passing secrets.SLOWER_ANALYZE_QUEUE=<queue> runs analysis as unique-per-record background jobs (dashboard + slower:analyze --queue); unset stays synchronous, no worker required. Jobs drop cleanly if their record was pruned.SlowQueryCaptured and SlowQueryFirstSeen (per-connection identity) — wire alerts to Slack/mail/webhook in a few lines. A throwing listener is reported but never breaks the app query, arms the circuit breaker, or suppresses the other event.Three new config blocks total: capture, ai_payload, analyze_queue.
Selected from a multi-model council roadmap. Deliberate choices: fingerprints from parameterized SQL (never raw), row-per-event storage kept (aggregate model deferred to v4.0), fingerprint normalizer favors a false split over a false merge (documented escaping tradeoff), origin backtrace paid only on slow queries.
require block.Publish and run the new migration, then optionally backfill fingerprints:
php artisan vendor:publish --tag="slower-migrations"
php artisan migrate
php artisan slower:fingerprint
Heads-up: GitHub Actions is still failing to provision runners account-wide (unrelated to this change), so the matrix was validated locally across Laravel 11–13 rather than by CI.
A documentation-only release. No code, configuration, or behavior changes — the package, config surface, and public API are identical to v3.1.0.
.env block — the two required lines (SLOWER_AI_SERVICE + the provider's API key) up front, every optional override commented out with its real Prism default (URL, organization, project, API version).Every environment variable and model default is verified against config/prism.php and AiServiceManager — not guessed.
Slower now works with every major LLM provider through Prism, with fewer config variables than before — provider credentials move out of Slower's config entirely.
SLOWER_AI_SERVICE=openai|anthropic|gemini|ollama|…. Any Prism provider works; a fully custom backend registers via AiServiceManager::extend().config/prism.php / OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY) — Slower's own open_ai config block is gone.gpt-5.4-mini, claude-haiku-4-5, gemini-2.5-flash), overridable with SLOWER_AI_RECOMMENDATION_MODEL.openai-php/laravel with prism-php/prism — one official package for all providers. The entire integration lives behind a single PrismDriver; the AiServiceDriver contract and AiServiceManager::extend() seam are unchanged, so Prism (and its pre-1.0 API) is isolated to one class.AiServiceManager maps ai_service → any Prism provider (Provider::tryFrom), with extend() taking precedence and unknown names throwing a helpful error.null return means only "no usable text"), so failed analyses stay retryable exactly as before.Existing OpenAI users need no changes — Prism reads OPENAI_API_KEY, and a boot-time bridge still honors a legacy slower.open_ai.api_key. Minor release.
PrismDriver tested with Prism::fake() (provider/model/prompt routing, empty→null); manager resolution, custom extend (via the real singleton path), the create{Name}Driver() BC path, and config all covered.extend()-singleton failure (custom LLMs silently ignored), wrong per-provider model defaults (OpenAI model sent to other providers), a dropped create{Name}Driver() extension convention, and misleading error messaging. Two residual items were reasoned design choices, not defects: key validation is deferred to call-time (an eager guard would break keyless providers like Ollama), and the 30s request timeout is a sane default (raise PRISM_REQUEST_TIMEOUT if needed).Heads-up: GitHub Actions is currently failing to provision runners account-wide (unrelated to this change), so CI hasn't validated the matrix here — it was validated locally across Laravel 11–13.
Platform modernization. No public API, config, or database changes — only the supported runtime and the dev toolchain moved forward.
casts() model method, so Laravel 10 was effectively unsupported — this makes the constraint honest.openai-php/laravel raised to ^0.20.0 — the previous ^0.18.0 capped at Laravel 12 and silently blocked Laravel 13 installs.pest-plugin-laravel 4 (first line to support Laravel 13), PHPStan 2 / larastan 3 (resolving the PHPStan 1-vs-2 dependency conflict), testbench 9–11.notify() hook in SlowerServiceProvider and its call site.No application-code, config, or migration changes are required if you already run PHP 8.3+ and Laravel 11+. Still on PHP 8.2 or Laravel 10? Stay on the ^2.x line.
Full changelog: https://github.com/halilcosdu/laravel-slower/blob/main/CHANGELOG.md
Install the package and you now have a full slow-query dashboard at /slower — no npm, no CDN, no assets to publish.

/slower): overview stats (total / pending / avg / max duration), searchable and filterable query list (status, connection), sortable columns, pagination, and a detail page with keyword-formatted SQL, bindings, and the AI recommendation rendered from markdown.analyze_pending_limit pending queries at once, delete one, and clean up older than N days (0 clears all). AI actions warn about provider charges; destructive actions confirm first.viewSlower gate defaults to the local environment only and is config:cache-safe. Define it in a service provider to open the dashboard in production.prefers-color-scheme, persists the choice), copy-to-clipboard, confirmations, auto-submitting filters.MarkdownRenderer: a tiny escape-first renderer that HTML-escapes all input before any transform, so AI recommendations render richly without becoming a stored-XSS vector.dashboard config block (enabled, path, domain, middleware, per_page, analyze_pending_limit). Existing keys are unchanged.Purely additive — no migration required, and the dashboard is disabled outside local by default. After upgrading, visit /slower locally, or define a viewSlower gate for other environments.
Security: captured SQL/bindings can contain sensitive data, and analyzing a query sends it to your AI provider as a billable call. Keep the gate tight and prune regularly with
slower:clean.
Full changelog: https://github.com/halilcosdu/laravel-slower/blob/main/CHANGELOG.md
Maintenance and quality pass. No public API breaks. Reviewed jointly with Codex/GPT-5.5.
RecommendationService now resolves the captured query's own connection and runs a non-executing EXPLAIN (never EXPLAIN ANALYZE), with a per-driver statement form (pgsql/mysql → EXPLAIN, sqlite → EXPLAIN QUERY PLAN, others skipped). Multi-statement input is rejected and EXPLAIN failures are reported without breaking analysis. The previous explain analyse was Postgres-only and could actually execute captured production SQL (e.g. UPDATE/DELETE).is_analyzed when the AI returns a non-empty recommendation. Empty results stay is_analyzed=false and are retried on the next scheduled slower:analyze. The command now prints an Analyzed | Skipped summary.createRecord reports failures via report() (without the raw SQL) and catches Throwable, so logging slow queries can never break the request.slower:clean and slower:analyze switched to chunkById.recommendation_model: deprecated gpt-4 (shut down 2026-10-23) → gpt-5.4-mini. Pin SLOWER_AI_RECOMMENDATION_MODEL=gpt-4 to keep old behaviour.openai-php/laravel → ^0.18.0, dependabot/fetch-metadata → 2.5.0.slower.php, ai_service driver switch documented, broken third-party screenshot removed, upgrade note added.OpenAiDriver now type-hints OpenAI\Contracts\ClientContract (substitutable/fakeable).RecommendationServiceTest, extended CommandsTest, SlowLogFactory). Suite: 39 passed. PHPStan level 5: clean. Pint: clean.Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v2.1.0...v2.2.0
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v2.0.4...v2.0.5
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v2.0.3...v2.0.4
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v2.0.2...v2.0.3
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v1.1.1...v2.0.0
Full Changelog: https://github.com/halilcosdu/laravel-slower/compare/v1.0.7...v1.0.8
Full Changelog: https://github.com/halilcosdu/laravel-slower/commits/v1.0.0
How can I help you explore Laravel packages today?