Why observability matters for Laravel microservices
When a Laravel API begins handling thousands of requests per second, a single latency spike can cascade into a full outage. According to a 2023 Cloud Native Survey, 68% of teams blame lack of distributed tracing for delayed incident resolution. Implementing OpenTelemetry gives you a single source of truth for logs, metrics, and traces, turning vague alerts into actionable data.
System requirements
Before adding any instrumentation, confirm that the host runs PHP 8.1 or higher, Composer 2.x, and a recent version of Laravel (9.x or later). The observability stack also needs Docker or native installations of Jaeger (v1.46+), Prometheus (v2.45+), and Grafana (v10+). Allocate at least 512 MiB of RAM for Jaeger and 256 MiB for Prometheus on a development box.
Install the OpenTelemetry SDK for Laravel
The easiest entry point is the community package open-telemetry/opentelemetry. Run the following command from your project root:
composer require open-telemetry/opentelemetryAfter the packages are downloaded, publish the configuration file so you can adjust exporters without touching code:
php artisan vendor:publish --provider="OpenTelemetry\Laravel\ServiceProvider" --tag="config" The generated config/opentelemetry.php contains placeholders for Jaeger, Prometheus, and custom resource attributes.
Configure Jaeger tracing exporter
Open the published config and set the exporter to jaeger. Provide the collector endpoint that matches your Docker network, for example http://jaeger:14268/api/traces. Laravel will automatically start a span for each incoming HTTP request, controller action, and queued job.
return [
'exporter' => 'jaeger',
'jaeger' => [
'endpoint' => env('JAEGER_ENDPOINT', 'http://jaeger:14268/api/traces'),
'service_name' => env('APP_NAME', 'laravel-app'),
],
];Remember to add JAEGER_ENDPOINT to your .env file. When you fire a request to /api/users, Jaeger UI will display a trace tree that includes middleware, database queries, and external HTTP calls.
Expose Prometheus metrics from Laravel
OpenTelemetry ships a PrometheusExporter that listens on a dedicated endpoint. Add a route in routes/web.php that returns the metric payload:
use Illuminate\Support\Facades\Route;
use OpenTelemetry\Exporter\Prometheus\PrometheusExporter;
Route::get('/metrics', function () {
$exporter = new PrometheusExporter();
return response($exporter->export(), 200)
->header('Content-Type', PrometheusExporter::CONTENT_TYPE);
});Prometheus scrapes this endpoint every 15 seconds. A minimal prometheus.yml job looks like:
scrape_configs:
- job_name: 'laravel'
static_configs:
- targets: ['host.docker.internal:8000']
metrics_path: '/metrics'
scheme: httpAfter restarting Prometheus, you will see counters such as http_requests_total and latency histograms automatically populated by the SDK.
Create Grafana dashboards for Laravel health
Grafana can query both Jaeger and Prometheus. Import a community dashboard (ID 11074) and adjust the datasource to point to your Prometheus instance. Add panels that plot http_requests_total by route, php_fpm_processes_active, and a trace list widget that pulls recent spans from Jaeger via the TraceQL endpoint.
For a quick visual, set a threshold on the 95th‑percentile latency panel; Grafana will fire an alert when the value exceeds 300 ms, sending a Slack webhook that includes the offending trace ID.
Testing the observability pipeline
Run a load test with hey -z 30s -c 20 http://localhost:8000/api/orders. After the test, open Jaeger UI and search for the service name you defined. You should see dozens of spans, each containing tags like http.method, db.statement, and custom attributes you added via Tracer::getCurrentSpan()->setAttribute(). Verify that Prometheus reports a spike in http_requests_total and that Grafana reflects the same period on the time series chart.
Best practices and pitfalls
1. Keep the sampling rate low in production (e.g., 0.1) to avoid overwhelming Jaeger. Use OTEL_TRACES_SAMPLER=parentbased_traceidratio in .env.
2. Mask sensitive data before setting span attributes; GDPR‑compliant applications should never log raw user identifiers.
3. Bundle the exporter configuration with your CI pipeline so every environment (staging, production) uses the correct endpoints.
4. Regularly prune old traces in Jaeger; the default 48‑hour retention can fill a 10 GiB volume within days for high‑traffic APIs.
Conclusion
By wiring Laravel OpenTelemetry to Jaeger, Prometheus, and Grafana, you gain end‑to‑end visibility across request latency, resource consumption, and error propagation. The setup described here requires only a few Composer commands, a couple of configuration tweaks, and a Docker compose file for the observability stack. Once in place, developers can pinpoint bottlenecks in milliseconds instead of hours, and operations teams can automate alerts based on concrete trace IDs. The result is a resilient Laravel ecosystem that scales confidently.
Sources
OpenTelemetry PHP Documentation; Jaeger Tracing Official Guide; Prometheus Monitoring Best Practices.
Author: Mahmut Sarıkaya — sarikayadev.com
.jpeg&w=320&q=50)