Observability
OpenTelemetry in a Laravel + Node.js Stack — A Practical Guide
The Problem with Polyglot Observability
Before OpenTelemetry, we had:
- PHP/Laravel → custom log parser feeding Elastic
- Node.js services → Winston logs to CloudWatch
- Go services → nothing (it was new)
Three different systems, no correlation between a frontend request and what happened across six downstream services. Debugging a slow booking flow meant opening four browser tabs and mentally joining log lines by timestamp.
What We Wanted
A single trace that follows a user request from the Laravel API gateway, through the Node.js availability service, into the Go pricing engine — with spans, errors, and timing at every hop.
[Laravel] → [Node.js Availability] → [Go Pricing] → [MySQL]
| | | |
200ms 45ms 12ms 8ms
Instrumenting Laravel
We used the open-telemetry/opentelemetry-php SDK. The key is auto-instrumentation for HTTP and PDO:
// config/otel.php
return [
'dsn' => env('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://otel-collector:4318'),
'service_name' => env('OTEL_SERVICE_NAME', 'booking-api'),
];
// AppServiceProvider.php
use OpenTelemetry\SDK\Trace\TracerProviderFactory;
public function boot(): void
{
$tracerProvider = (new TracerProviderFactory())->create();
// Register globally — auto-instrumentation picks this up
\OpenTelemetry\API\Globals::registerInitializer(
fn() => $tracerProvider
);
}
Results
| Metric | Before | After |
|---|---|---|
| Mean debug time for cross-service issue | 45 min | 4 min |
| P95 trace coverage | 0% | 94% |
| Alert MTTR | 38 min | 11 min |
The first week after rollout we found three latency issues we didn’t know existed. One of them had been silently degrading the checkout flow for four months.