scoutapp/scout-apm-php
Scout APM PHP is the official monitoring agent for Scout, giving Laravel and plain PHP apps performance tracing, error tracking, and slow query insights with lightweight, low-overhead instrumentation so you can find and fix bottlenecks fast.
Minimal path from install to first trace:
composer require scoutapp/scout-apm-php. In a Laravel app you will almost never install this directly — install scoutapp/scout-apm-laravel, which pulls this package in and wires the service provider, middleware, and job/command instrumentation for you.pecl install scoutapm and enable extension=scoutapm.so. The pure-PHP fallback works, but the extension provides the bulk of auto-instrumentation (PDO, curl, file I/O, Redis, etc.) and lower overhead. Verify with php -m | grep scoutapm.SCOUT_KEY, SCOUT_NAME (app name shown in the dashboard), SCOUT_MONITOR=true, and SCOUT_ENV (production/staging). Set SCOUT_MONITOR=false (or omit the key) in local/test.src/Agent.php (the static façade — connect, instrument, webTransaction, backgroundTransaction, addContext, tag), src/Transaction.php (per-request state, span tree, tags/context), and src/Instrument/ (the auto-instrumentation classes the extension/pure-PHP layer drives). The Laravel middleware largely just calls Agent::webTransaction() and Agent::send() around the request.Agent::isConnected() returns true, a transaction appears in the Scout UI), then wrap your slowest business-critical path — a report generator, third-party API call, or export job — with a named instrument and add searchable context.use ScoutApm\Agent;
Agent::instrument('report.generate', function () use ($user) {
Agent::addContext('user_id', $user->id);
Agent::tag('plan', $user->plan);
return $this->buildReport($user);
});
Agent::instrument($name, $callable). Nested calls produce child spans automatically, so you get a useful waterfall (controller → service → query) without manual span bookkeeping. Name instruments after stable, low-cardinality concepts (report.generate, not report.generate.42).Agent::tag('key', $value) creates a searchable/filterable dimension in the UI — use it for status codes, tenant tier, queue name, feature flag. Agent::addContext() attaches detail to a single trace for debugging — use it for IDs, payload sizes, cache hits. Never put unbounded IDs in tags.Agent::backgroundTransaction('nightly:invoice-sync', function () {
// work
});
// or for non-HTTP request handlers:
Agent::webTransaction('webhook:stripe', function () { ... });
backgroundTransaction($job->resolveName(), ...) and send at the end — otherwise job work is either unattributed or lost.try/finally only if you're managing transaction lifecycle yourself.SCOUT_REVISION_SHA (and SCOUT_SCM_SUBDIRECTORY for monorepos) in CI so the dashboard can flag regressions against a deploy. This is the single highest-value non-code config for day-to-day triage.SCOUT_LOG_LEVEL=debug and SCOUT_MONITOR=true against a staging key; watch the agent log for connection/serialization issues, then flip to false for app-level unit tests.ext-scoutapm, expect materially fewer auto-instrumented libraries and some overhead. Budget explicit Agent::instrument() wrapping around PDO/HTTP/file-heavy code, or your traces will be sparse and misleading.SCOUT_MONITOR is falsy or SCOUT_KEY is missing, everything becomes a no-op with no errors. Always assert Agent::isConnected() in a smoke test after deploy rather than assuming data is flowing.SCOUT_MONITOR="false" can be truthy depending on how they're parsed. Cast booleans explicitly in config/scout.php and never rely on the string alone.instrument() / addContext() call in middleware that runs after the response has been flushed (late terminable middleware, garbage collection hooks, shutdown handlers) will be dropped. Place instrumentation inside the request/job lifecycle, before Agent::send().webTransaction/backgroundTransaction wrappers) actually reset state.SCOUT_LOG_LEVEL=debug, tail the agent log, and confirm connectivity/collector host reachability from the runtime environment. Firewall egress to collector.scoutapm.com from production is a common first-deploy failure.src/Instrument/ follows the Instrument contract — custom data stores or SDKs can be integrated by implementing that interface and registering it, mirroring how the bundled PDO/curl/file instruments are structured. Use this rather than ad-hoc timing wrappers when you need first-class span names and SQL/HTTP metadata.How can I help you explore Laravel packages today?