affordablemobiles/open-telemetry-operations-php
Operational tooling for OpenTelemetry in PHP applications. This package provides helpers to configure and manage tracing and metrics, making it easier to instrument Laravel/PHP services and ship telemetry data to your observability backend.
Before writing a line of code, confirm three things: PHP 8.1+, open-telemetry/sdk ^1.x, and a GCP project with the Cloud Trace API and Cloud Logging API enabled plus Application Default Credentials available to the process.
composer require affordablemobiles/open-telemetry-operations-php
Where to look first:
src/ for the span exporter (a SpanExporterInterface implementation, typically *CloudTraceExporter), the Monolog handler/formatter for Cloud Logging, and any trace-context propagator.composer.json for the exact open-telemetry/* version constraints — mismatches with your app's SDK version are the most common install failure.First use case: don't start with middleware. Write a throwaway Artisan command that builds a TracerProvider with a SimpleSpanProcessor and the package exporter, creates one span, ends it, and calls $provider->shutdown(). Run it, then check the Cloud Trace console. This isolates auth, project-ID, and payload-shape problems from HTTP lifecycle problems before you add either.
TracerProviderInterface and TracerInterface as singletons in a TelemetryServiceProvider registered in config/app.php (or bootstrap/providers.php on Laravel 11). Use BatchSpanProcessor in production and SimpleSpanProcessor locally so you see spans immediately while debugging.http.request.method, url.path, http.response.status_code, and end the span in a finally block. Register it before TrimStrings/SubstituteBindings so the span covers the full request.logging.googleapis.com/trace, logging.googleapis.com/spanId, and logging.googleapis.com/trace_sampled, pulled from the currently active span. This is what makes the "View Trace" link work from a log line in the GCP console.JobDispatched/JobProcessing listener pair is usually enough.Illuminate\Console\Events\CommandStarting/CommandFinished to create a root span per command, which covers scheduled tasks with no extra effort.ParentBased(TraceIdRatioBased)) and let both the exporter and the log formatter read the same trace_sampled flag. Divergent sampling produces logs that point at traces that were never exported.$provider->shutdown() (or forceFlush()) in a Terminating listener, and again in the Octane worker's terminating hook. Without it, BatchSpanProcessor drops everything in its queue on every request.php artisan vendor:publish will do anything.Scope::detach() plus clearing baggage), or span trees will nest under a previous request's parent.php artisan serve doesn't scream credential errors on every request.GOOGLE_CLOUD_PROJECT rather than relying on the default.logging.googleapis.com/trace must be projects/{project}/traces/{32-hex-id}. A truncated or mismatched ID breaks the console jump with no error.X-Cloud-Trace-Context (TRACE_ID/SPAN_ID;o=1), not W3C traceparent. If you only parse the W3C header, every inbound production request starts a new trace.$processors argument position). Pin your Monolog major and verify the handler instantiates before deploying.open-telemetry/exporter-otlp pointed at Google's OTLP endpoint as a swap-out plan if the package stalls.InMemorySpanExporter and assert against finished spans.recordException() needs a Throwable; PSR-3 context cannot hold objects. Flatten exception data (class, message, code) before handing it to the logging handler or it will throw.How can I help you explore Laravel packages today?