inspector-apm/inspector-php
Inspector is a code execution monitoring tool for PHP developers. It tracks transactions and code segments in real time, alerts you when something goes wrong, requires PHP 8.1+, installs via Composer, and supports custom transports.
inspector-apm/inspector-php is a pure-PHP SDK (no daemon, no extension, no sidecar). It creates a Configuration + Inspector object, opens a transaction per execution cycle, and nests segments via addSegment(callable, label). That maps cleanly onto Laravel's request lifecycle, queued jobs, artisan commands, and scheduled tasks — the four execution contexts our TPM roadmap usually has to cover.fork() scope primitive (v3.17.0) is the technically interesting differentiator. It gives each concurrent task its own segment stack, so Fibers / ReactPHP / Amp / pcntl_fork workers keep correct parent-child relationships instead of corrupting a shared stack. For teams running queue workers and event loops, this removes a real class of "the trace is a lie" bugs that plague naive APM integrations.registerGlobalHandler() (v3.16.7) auto-registers with PHP's global exception handler — low-effort coverage of unhandled exceptions, but it is a process-global side effect that needs to be reasoned about in apps that already install their own handlers (Laravel's Handler, Sentry, etc.).TransportInterface + setTransport(callable) is the escape hatch that matters to a TPM. It means we are not hard-locked to Inspector's ingest endpoint; we can route data elsewhere (or no-op in tests/CI) without forking the library. That is the difference between "vendor decision" and "architecture decision."composer require, instantiate with an ingestion key, wrap the hot path or add the middleware. A single service can be producing traces in under a day; a credible pilot with dashboards and alerts is a 1–2 week effort for one engineer.registerGlobalHandler() can fight with existing error handlers. Needs explicit ordering and a rollback plan.TransportInterface sufficient to redirect to an internal collector, or does the segment/transaction model assume Inspector semantics?startTransaction and addSegment. More work, same model.TransportInterface swap works by pointing at a no-op or internal sink in CI. If we can't do it in an afternoon, we don't have an exit and the "MIT, no lock-in" argument is hollow.registerGlobalHandler, fork scopes).Handler, Sentry), existing tracing (Telescope, OTel), HTTP client instrumentation, and queue driver.| Phase | Duration | Deliverable | Gate |
|---|---|---|---|
| 0. Baseline + ADR | 3–5 days | ADR, baseline metrics, data-flow review | Security/legal sign-off on egress |
| 1. Staging shadow | 1 week | Traces for 1 service, overhead measurement | p95 overhead < 3%, traces legible |
| 2. Prod canary | 1–2 weeks | One endpoint/job class traced live | No new incidents attributable to SDK |
| 3. Full service + alerts | 1 week | Dashboards, 2–3 actionable alerts | On-call accepts the alert set (no noise) |
| 4. Decision | 1 week | Go/no-go memo with cost, exit test result | Sponsor decision on broader rollout |
The critical sequencing rule: dashboards and alerts must be defined before production rollout, otherwise the pilot ends with pretty charts nobody watches and we've spent the budget for zero decisions.
composer update plus a regression pass on the pilot service. Budget roughly a half-day per quarter for version currency, more if we adopt new features (fork scopes, global handler).db, Database, db-query). Enforce a naming convention early, or the data becomes unqueryable at exactly the moment you need it.addSegment calls can break trace continuity. Document the convention and keep instrumentation in a thin, reviewable layer rather than sprinkled through business logic.setTransport to build one that does. This is the single most important pre-production check for high-QPS services.| Failure | Symptom | Blast radius | Mitigation |
|---|---|---|---|
| Inspector ingest unreachable | Traces dropped, app unaffected (ideal) or requests slow (bad) | Latency if flush is synchronous | Short cURL timeout, async flush, fail-open design |
| Bad ingestion key / quota exceeded | Silent data loss | Observability blind spot | Synthetic check on dashboard data freshness; alert on "no traces received" |
| Global handler conflict | Exceptions swallowed or double-reported | Error visibility, potentially request handling | Order handlers explicitly; test with real thrown exceptions |
| Instrumentation exception | Uncaught error inside a segment | User-facing 500 | Wrap all instrumented callables defensively; never let tracing throw |
| Async scope leak | Orphaned/misparented spans, growing memory in workers | Skewed traces, worker OOM | Use fork() scopes consistently; audit getOpenSegments() in long-running workers |
| Vendor pricing change or shutdown | Cost spike or forced migration | Budget and roadmap | Exit test via TransportInterface; keep the ADR's fallback documented |
The recurring theme: every failure mode should be fail-open. Observability must never be able to take down the thing it observes. If the design can't guarantee that, the pilot isn't ready for production.
How can I help you explore Laravel packages today?