Three agent architectures
1. In-process (library)
A composer package that registers inside your Laravel app. Captures telemetry synchronously during the request and ships it via HTTP after the response. Examples: Laravel Telescope, Sentry's default PHP SDK, Flare's SDK, Inspector.dev's Laravel package.
Pros: easy install (composer require + done), no extra process to supervise, works on any host.
Cons: overhead hits every request (typically 5-50ms), HTTP shipping can block on vendor outages if not queued, accumulates memory on persistent workers like Octane.
2. Out-of-process (daemon)
A separate long-running process — usually a ReactPHP daemon or system service — that buffers telemetry from your Laravel app and ships it to the dashboard asynchronously. Examples: NightOwl's agent, Laravel Nightwatch package's agent, Datadog's Lambda Extension.
Pros: sub-1ms request-path overhead, crash isolation (agent crash doesn't take down your app), graceful handling of vendor outages (buffers telemetry locally).
Cons: need to supervise the extra process (Supervisor, systemd, Forge daemons), slightly more setup, doesn't fit serverless like Lambda without a Lambda Extension variant.
3. System extension (PHP extension)
A compiled PHP extension that hooks into the runtime at a low level. Can capture data userland PHP can't see — opcache stats, runtime-internal timings, zero-overhead stack sampling. Examples: New Relic PHP agent, Tideways PHP extension.
Pros: maximum data richness, often lowest per-request overhead, captures things composer packages can't.
Cons: requires root access to install, has to match your PHP version exactly (breaks on upgrades), ties you to the vendor.
How NightOwl's agent works
Laravel request flow
├── Request arrives at PHP-FPM / Octane worker
├── Nightwatch package observes events (queries, jobs, exceptions, etc.)
├── Events serialized to a local TCP socket (port 2407)
├── Worker returns response to user ← latency budget ends here
│
└── NightOwl agent (separate ReactPHP process)
├── Receives events asynchronously from all app workers
├── Buffers in-memory queue
├── Batch-writes to your PostgreSQL via COPY protocol
└── Retries on transient failuresRequest-path overhead: serialize + socket write = under 1ms. All heavy lifting (validation, batching, PostgreSQL COPY) happens in the separate agent process. Benchmarked at 13,400 payloads/s single-instance.
Why architecture matters for Octane
In Octane, a worker handles thousands of requests without dying. In-process agents accumulate state — event buffers, retry queues, observer registrations — across every request the worker handles. Memory grows; eventually the worker OOMs or slows.
Out-of-process agents immunize you: the agent's state is in its own process; the Laravel worker holds nothing across requests beyond a socket connection. This is why NightOwl's architecture is Octane-native and why some SDK-based APMs require extra work to be Octane-safe.