Distributed tracing for PHP services, explained
Distributed tracing for PHP shows one request's whole journey across services. Learn traces, spans, and propagation — and how to see it end to end.
When your app was one Laravel monolith, a stack trace told the whole story. Add a queue worker, a second service, and a third-party API, and that story fractures: the request that felt slow to the user touched four processes, and no single log file saw all four. Distributed tracing for PHP exists to stitch those pieces back into one timeline. Instead of guessing which hop was slow or where the error really started, you see the entire journey of a single request as one connected picture. This post explains the core ideas — traces, spans, and propagation — and how to get them flowing without rewriting your app.
Traces and spans
A trace is one request's complete journey through your system. A span is a single unit of work inside that trace — a controller action, a database query, an HTTP call to another service, a job run on a worker. Spans nest: the controller span contains the query span and the outbound-HTTP span; that outbound call becomes the parent of a span in the next service.
Each span records a start time, a duration, and metadata (the SQL, the URL, the status). Lay them out and you get a waterfall: a visual timeline where a long bar is exactly the slow part. You stop asking "is it the database or the API?" because the waterfall answers it.
Why one service isn't enough
Single-service timing tells you a request took 1,200ms. It doesn't tell you that 900ms of that was a downstream service waiting on its database. Without tracing across the boundary, every team blames the network and nobody owns the fix. Distributed tracing assigns the time to the span that actually spent it, across every service the request touched — so the slow hop has a name and an owner.
Propagation: how the trace crosses the wire
The magic is context propagation. When service A calls service B, it sends a small set of headers — a trace ID and the current span ID — along with the request. Service B reads those headers and continues the same trace instead of starting a new one. That shared trace ID is the thread that ties every span together.
In a Lookout-instrumented Laravel app, outbound HTTP carries this context automatically, and on the receiving side you continue the trace with the lookoutTracing.continueTrace middleware:
Route::middleware('lookoutTracing.continueTrace')->group(function () {
Route::post('/charge', ChargeController::class);
});
The receiving service now records its spans under the same trace as the caller, so both halves show up in one waterfall.
What you actually see
With traces flowing, a slow or failing request gives you:
- A waterfall of every span, longest bars highlighted
- The exact query, URL, or job behind each span
- The service boundary where time was spent or an error was thrown
- A service map: which of your services call which, and where the failures cluster
That last view turns "we have a microservices problem" into "the billing service's call to the tax API is the slow edge."
Getting it running
You don't need an agent or a sidecar. Install the SDK, point it at your project, and enable the performance middleware:
composer require lookout/tracing
php artisan lookout:install
LOOKOUT_LARAVEL=true
LOOKOUT_API_KEY=your-project-api-key
LOOKOUT_URL=https://your-lookout-host.example.com
Add the performance middleware to the routes you care about, and register lookoutTracing.continueTrace on services that receive traced calls. Ingest is plain HTTP and JSON, so non-Laravel PHP services (and Node, Ruby, Go, and more) can join the same trace.
Wrapping up
Distributed tracing for PHP turns a request that disappears into a fog of services into a single, readable timeline. Traces hold the journey, spans hold the work, and propagation carries the trace ID across every hop so it all reconnects. Once you can see the waterfall, the slow span names itself. Start tracing your services free — 10,000 events a month, one project, no credit card.