Laravel performance, in your terminal
Inspect Laravel performance locally. Catch regressions in CI.
Find slow routes, repeated queries, and performance regressions from your local test workload.
| Route | Baseline (main) | Current (Workload) | Change |
|---|---|---|---|
| orders.index | p95 182ms · 2q | p95 241ms · 51q | +32.4% ⚠ +49q ✗ |
| orders.show | p95 94ms · 3q | p95 91ms · 3q | -3.2% ✓ |
| users.index | p95 45ms · 8q | p95 48ms · 27q | +6.7% +19q ⚠ |
| dashboard | p95 310ms · 4q | p95 305ms · 4q | -1.6% ✓ |
app/Http/Controllers/OrderController.php:42 Order::with('customer') | Route | P95 | Peak RAM | Status | Repeats |
|---|---|---|---|---|
| 1.42s | 24.2 MB ⚠ | Needs Work | N+1 ×42 | |
| Executed 42× with varying IDs: app/Http/Controllers/OrderController.php:84 select * from `items` where `order_id` = ? Suggested fix: Order::with('items') | ||||
| 34ms | 4.1 MB | Healthy | CACHE ×3 | |
| Executed 3× with identical bindings: app/Providers/AppServiceProvider.php:32 select * from `settings` where `key` = ? Suggested fix: Cache::remember('app_settings', 3600, fn() => ...) | ||||
| api.orders.show | 89ms | 6.2 MB | Healthy | — |
- N+1 violation:
Relationship
Order → itemsexecuted 42× with varying IDs. - Query budget exceeded: 42 queries executed (configured budget: 20 queries).
- Trigger:
app/Http/Controllers/OrderController.php:84
Measured Runtime Overhead
In benchmark testing, in-memory query and route listening added ~0.3–0.4 ms
per request across 200 requests (10 queries/req in included Testbench suite). Persistence writes are deferred to Laravel terminating callbacks after HTTP dispatch.
When a refactor silently changes query behavior
pinpoint:diff compares recorded PR metrics against a baseline snapshot (main) and exits 1 on regression.
42 queries · 14× lazy-loaded relations
3 queries · Order::with('items')
pinpoint:report Identical SQL + Identical Bindings
Same query executed multiple times during the request. Review whether the result can be reused or avoided.
Cache::remember(), memoization, or query reuse
Repeated SQL + Varying Bindings
Repeated SQL with varying bindings can indicate a relationship being loaded once per model.
Model::with('relation') Core Capabilities
Focused performance diagnostics for local dev and CI.
Pinpoint correlates recorded queries and requests with available source locations so you can trace a finding back to the code that triggered it.
Repeated queries & N+1 candidates
Find repeated queries and N+1 candidates in your recorded workload. Repeated queries with identical bindings are grouped separately from varying-binding N+1 candidates so you can decide whether to reuse, memoize, cache, or remove the query.
Performance regression diff
Compare the current workload against a recorded baseline and report changes in latency, query count, repeated queries, and memory.
Route latency & percentile rollups
Report p50, p95, p99, and average request duration by route. Summaries can be computed on demand from recorded request rows or snapshotted for regression diffing.
Peak PHP memory per route
Flag requests whose measured peak PHP memory exceeds the configured budget ('memory_budget_kb' => 20 * 1024); baseline diffs also detect memory growth between runs.
Source-level context & IDE jump
Where source locations are available, Pinpoint emits terminal links that can open the reported file and line in supported IDEs (VS Code · PhpStorm). Local-first: data stays in your database.
Order::with('items') Cache::remember, memoization, or query reuse
CI Regression Gate
Fail CI when a recorded workload violates your performance budget.
Automate performance checks in CI. Exit with code 1 when query budgets explode or performance regresses against a named baseline.
pinpoint:snapshot --file=storage/pinpoint/baselines/main.json and cached for PR jobs. Decoupled from application database state.
Order → items lazy-loaded inside collection loop.
Example output from a Laravel application.
Illustrative report showing latency, memory, and repeated-query findings.
v1.historical-records.search clocked at 4,350ms, flagged in the red tier.
Detects relationship loops and duplicate executions up to x11 queries per HTTP request.
The CLI can render route labels and caller locations as terminal links that open your IDE.
Where Pinpoint Fits
A focused tool for your terminal and CI.
Pinpoint doesn't replace browser debuggers or deep system monitors. It complements them by focusing specifically on terminal request inspection, baseline diffing, and automated CI performance gates.
| Workflow & Use Case | Pinpoint | Recommended Ecosystem Tool |
|---|---|---|
| Terminal Request Reports & Tiers p95/avg latency percentiles, memory alerts, and IDE jump links | ✔ pinpoint:report | Primary use case for local CLI profiling |
| Named Baseline Diffs Compare current test runs against a tagged snapshot (e.g. main) | ✔ pinpoint:diff | Automated regression delta detection |
| CI Performance Budget Gates Exit code 1 on N+1 queries or query count budget violations (blocks CI when required) | ✔ pinpoint:check | Headless CI check without browser or UI |
| Browser Request & DOM Inspection In-page tabs for rendered views, request headers, and live queries | — | Use Laravel Debugbar |
| Queues, Jobs, Events & Mails Deep application lifecycle insight via local web dashboard | — | Use Laravel Telescope |
Pinpoint is focused on local development and CI regression checks; it is not a distributed tracing or production alerting system.
Quickstart & Commands
Get started in minutes.
Install, migrate, run your app. Then one command tells you what's slow.
Install the package
composer require asimali/pinpoint Publish & run migrations
php artisan vendor:publish --tag="pinpoint-migrations" && php artisan migrate Generate your first latency report
php artisan pinpoint:report After running migrations, hit the routes you want to profile — via browser, curl, or tests — to generate data for the report.
Core Artisan Commands
The essential commands for local diagnostics, CI gating, and baseline diffing.
Generate terminal summary tables with latency rollups (p50/p95/p99/avg), peak memory per route with budget alerts, N+1 vs CACHE classification (exact-duplicate detection), terminal IDE links, and machine-readable JSON payloads.
--tier=critical --route=<name> --since=30m --limit=10 --json --json-to=<file> Compare current test run performance against a baseline snapshot (main). Flags regressions in p95 latency, repeated-query increases, and peak memory in terminal and CI with exit code 1.
--baseline=main --since=30m --fail-on-regression --json --show-stable Evaluate query counts, durations, and repeated-query violations (N+1 candidates and duplicate queries) as an automated CI gate, exiting with code 1 upon violation.
--fail-on-n1 --fail-on-repeat --max-queries=<n> --max-duration-ms=<ms> --since=30m --allow-empty --json --json-to=<file> Capture current performance metrics as a named baseline tag or exported JSON file for future regression diffing (e.g. before merging to main).
--tag=main --file=<path.json> --since=1h --no-overwrite Maintenance Commands (aggregate, prune, reset) ▼
Compute raw request rollups and persist an offline percentile snapshot in pinpoint_summaries for external tools or reporting integrations (CLI reports compute on-demand from raw rows).
Delete recorded request and query data older than the retention window (default 30 days).
--days=30 Delete all recorded Pinpoint data (requests, queries, lazy loads, summaries) with confirmation prompt.
--force Start profiling your Laravel routes today.
Local-first: request data stays in your application database. Install, exercise a route, and inspect the resulting report.