keepsuit/laravel-opentelemetry
OpenTelemetry integration for Laravel: automatically instrument HTTP requests, database queries, queues, Redis, and more, then export traces and metrics via OTLP to your observability backend. Simple configuration and Laravel-native APIs.
Minimal steps
composer require keepsuit/laravel-opentelemetry
php artisan vendor:publish --tag=opentelemetry-config
OTEL_* environment variables. Start with a local or staging OTLP endpoint — an OpenTelemetry Collector, Jaeger, or Grafana Tempo — not a production backend.OTEL_SERVICE_NAME (use a stable, low-cardinality name such as checkout-api)OTEL_EXPORTER_OTLP_ENDPOINTdeployment.environment, service version, region)Where to look first
config/opentelemetry.php — instrumentation toggles, exporter settings, middleware, and any sampling/resource configuration.composer.lock — this is the real behavior boundary for exporters, batching, and semantic conventions.bootstrap/app.php / HTTP kernel and queue configuration, because that is where instrumentation hooks in and where context propagation can break.First use case
Pick one request path with a known latency or reliability problem — for example, “create order” or “fetch account summary.” Instrument that path end to end:
The goal of the first use case is not full coverage. It is to prove that a developer can answer “where did this request spend its time, and which dependency failed?” from the trace alone, without adding ad hoc logging.
Automatic-first, manual where it matters
Treat framework instrumentation as the baseline: HTTP requests, DB queries, queue jobs, Redis/cache, and outbound HTTP client calls. Do not manually re-instrument those. Reserve manual spans for domain behavior the framework cannot see:
Use manual spans at transaction boundaries, not around every private method. A useful rule: add a span when you would otherwise add a temporary timer or log line during an incident.
Span naming conventions
Agree on conventions before broad rollout:
payment-gateway.authorize.order.calculate_totals.Stable naming matters more than perfect naming. Dashboards and alerts will depend on it.
Request → DB → queue → outbound tracing
The primary day-to-day value is one trace showing the full path. Pattern:
Explicitly test each async boundary. Context propagation across queues, scheduled jobs, and console commands is where “distributed tracing” usually degrades into disconnected traces.
Queue and console jobs
For jobs, prefer one span per job and child spans per meaningful unit of work. Avoid creating a span per record in a large batch unless you are actively debugging that batch; it inflates volume and backend cost.
For scheduled/console commands, add a manual root span if the package does not create one automatically. Without it, command work appears as orphaned child spans or disappears from trace-based analysis.
Outbound HTTP integration
Instrument the shared HTTP client, not individual call sites. Then add application context at call sites through span attributes or events that describe the operation, not the payload. Include a deliberately failing outbound call in acceptance testing — failed-request spans are the ones that drive error-rate SLOs and incident triage.
Collector as the architectural boundary
Day-to-day developer workflow should assume:
Laravel app -> OTLP -> OpenTelemetry Collector -> backend
Developers instrument and emit; the platform team owns sampling, redaction, routing, and backend choice at the Collector. This keeps backend migration and attribute scrubbing out of application deploys. It also means developers should not hard-code vendor-specific endpoints or SDK exporters in application config.
Local development pattern
OTEL_SERVICE_NAME suffix such as -local and deployment.environment=local.Testing and rollout pattern
Pin a minimum of 2.2.7
Release 2.2.7 fixed HTTP client traces failing to record failed outbound requests. Treat anything ≤2.2.6 as an unreliable baseline for outbound error analysis. Require >=2.2.7 in any pilot or production constraint, and add a deliberately failing outbound request to your acceptance tests so this class of regression is caught locally.
Do not use a synchronous exporter
The classic production mistake is synchronous span export. Under Collector degradation it adds tail latency or turns telemetry loss into request failures. Use asynchronous/batched export with short timeouts, and make telemetry loss explicitly non-fatal to the request path.
Flush on shutdown, especially with long-lived workers
Under Octane, Swoole, or long-running queue workers, batch buffers and memory behave differently than under PHP-FPM. Verify:
Test with a synthetic job that is deliberately terminated mid-run.
Redact before production, not after
SQL text, query bindings, URLs, headers, and exception messages can carry PII or secrets. This is a policy and configuration problem, not just a package setting. Establish:
Prefer Collector-level scrubbing for defense in depth, but do not rely on it alone; avoid emitting sensitive values from application code in the first place.
Watch metric and attribute cardinality
Raw user IDs, full URLs, request IDs, and unbounded exception messages as span or metric attributes will damage backend performance and cost. Keep high-cardinality values as span attributes only where needed for debugging, and keep metric labels low-cardinality. If the package or your manual spans emit route paths, normalize them to route templates.
Verify context propagation across async boundaries
Do not assume queue, scheduled-job, and console propagation works because HTTP tracing does. Check parent/child relationships explicitly for:
Orphaned traces usually indicate missing context injection or a worker that starts a new root span.
Patch releases matter more than semver suggests
2.2.7 is a one-line-looking patch that changes what is visible in production error analysis. Read patch changelogs before bumping and treat package + OTel PHP SDK as a single upgrade unit. Pin them together and test upgrades together; upstream semantic conventions and SDK behavior still move.
Upstream SDK is part of your dependency surface
This package is an integration layer. Reliability depends on the OTel PHP SDK, exporter configuration, Collector, and backend. Check whether the native PHP extension is available in your build images; the pure-PHP path is simpler operationally but slower. Validate PHP version and extension availability in every runtime image, not just locally.
Auto-instrumentation will not cover domain logic
Expect gaps in pricing engines, saga orchestration, business-rule evaluation, and internal service boundaries. Manual spans are a developer-experience and training obligation. Publish short examples and a naming guide so different teams do not invent incompatible span names.
Adoption and support risk is real
Low stars, zero listed dependents, and effectively single-maintainer status mean you are an early production user. Mitigations:
Keep backend choice out of application code
Because the output is OTLP, the backend should be a procurement and Collector concern. Do not let vendor-specific exporters or endpoints leak into controllers, jobs, or package config. This preserves the main advantage of the package and keeps a backend migration to a Collector configuration change rather than an application rewrite.
Pilot success criteria should be explicit
Define them before rollout: end-to-end trace visibility for a named transaction, acceptable overhead, verified error-path coverage, documented redaction rules, queue/console coverage, and a named owner for the Collector and the package. Without these, the pilot becomes an open-ended instrumentation project.
How can I help you explore Laravel packages today?