Pulse is Laravel's first-party application performance dashboard: a single package, a single migration, and a dashboard route that shows slow requests, slow queries, queue throughput, exceptions, and server load without standing up Grafana or paying for a third-party APM. It's not trying to replace a full observability stack — it has no distributed tracing, no alerting engine, no long retention story by default — but for a small-to-mid-size Laravel app, it answers "what's actually slow right now" faster than wiring up OpenTelemetry ever will.
How Pulse collects data without slowing requests down
Pulse's core design decision is that recording shouldn't happen inline with the request that triggered it. Recorders capture entries (a slow query, a request duration, an exception) into an in-memory or Redis-backed buffer, and a separate `pulse:check` scheduled command or ingest process aggregates and writes them to storage. The upshot: even under load, Pulse's overhead per request is a lightweight write to a fast in-memory store, not a synchronous database insert competing with the request's own queries for connection pool slots.
// config/pulse.php
'recorders' => [
Recorders\SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'threshold' => 1000, // ms — only capture queries slower than this
'sample_rate' => 1,
],
Recorders\SlowRequests::class => [
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
'threshold' => 500,
'sample_rate' => 1,
],
Recorders\Queues::class => ['enabled' => true],
Recorders\Exceptions::class => ['enabled' => true, 'sample_rate' => 1],
],
'ingest' => [
'driver' => env('PULSE_INGEST_DRIVER', 'storage'), // or 'redis'
],
Sampling is not optional at scale
Every Pulse recorder supports a `sample_rate` between 0 and 1. At `sample_rate => 1`, every matching event gets recorded — fine for a low-traffic app, expensive for one doing thousands of requests a minute, since every recorded entry is still a row written somewhere. Dropping slow-query or exception sampling to 0.1 or 0.25 on a high-traffic endpoint keeps the signal (you'll still see the pattern) while cutting storage and write load by the same factor. The mistake is leaving every recorder at full sampling and then being surprised the pulse_entries table needs its own retention policy.
Run pulse:purge on a schedule (daily is typical) to trim entries past your retention window, set via PULSE_RETENTION config. Without it, the pulse_entries and pulse_aggregates tables grow indefinitely and the dashboard itself gets slower to render as it scans more history.
Custom recorders for business-specific signals
Pulse's recorder interface is extensible — you can write a custom recorder that captures something specific to your domain (payment webhook latency, a third-party API's response time, cache hit rate on a specific key pattern) and have it show up on the same dashboard as the built-in cards. This is where Pulse earns more of its keep than the out-of-box installation suggests: the built-in recorders answer generic Laravel questions, but a custom recorder tracking "time to process an incoming webhook" answers a question specific to your product that a generic APM dashboard wouldn't surface without custom instrumentation anyway.
What Pulse is not
Pulse has no distributed tracing across services — if a slow request is slow because it's waiting on a downstream microservice, Pulse shows you the slow request but not the trace into that service. It also has no built-in alerting; the dashboard is something a human has to look at, not something that pages someone at 2am. For a single Laravel monolith, that's often enough. For a system split across several services where you need cross-service traces and automated alerting, Pulse belongs alongside a real observability stack (Sentry for exceptions with alerting, an actual tracing backend), not instead of it.
By default Pulse's dashboard is gated behind a viewPulse authorization gate that you're expected to define — it ships permissive in local development but you must lock it down before deploying. Query duration, slow endpoints, and exception messages are the kind of information you don't want exposed to anyone who finds the /pulse route.
Wrapping up
Pulse gives you a real-time view of slow queries, slow requests, queue health, and exceptions with almost no setup cost, because its recorder-plus-scheduled-ingest design keeps collection overhead off the request path. Tune sample rates before you hit production traffic, purge old entries on a schedule, and treat it as a fast local diagnostic tool that complements — rather than replaces — a proper observability stack once your architecture spans more than one service.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.