Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Open Telemetry Laravel Package

overtrue/laravel-open-telemetry

OpenTelemetry integration for Laravel: instrument requests, queries, jobs, and more with traces, metrics, and logs using the official PHP SDK. Auto-configures providers and middleware so you can send telemetry to any OTLP-compatible backend with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish config

composer require overtrue/laravel-open-telemetry
php artisan vendor:publish --tag=open-telemetry-config

Set the minimum env needed to get traces out of the app:

OTEL_SERVICE_NAME=my-app
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_TRACES_SAMPLER=parentbased_always_on

Point it at a collector before touching code. The fastest loop is an all-in-one Jaeger or otel/opentelemetry-collector-contrib container receiving OTLP on 4317/4318. Half of all "the package isn't working" reports are actually a missing/unreachable collector, so validate the pipeline first and instrumentation second.

Where to look first

  1. config/open-telemetry.php — this is the whole surface area of the package: exporter settings, sampler, and which instrumentation groups (HTTP, DB, queue, Redis, console) are enabled.
  2. The package's service provider — check which middleware/listeners it auto-registers so you know what is traced for free versus what you must wrap yourself.
  3. Your bootstrap/app.php / Http/Kernel.php — the request middleware is what produces your first trace.

First use case: boot the app, hit a single route that touches the database, open Jaeger, and confirm you see one root HTTP span with child query spans and the correct attributes (http.route, db.system, db.statement). That one trace proves endpoint, service name, exporter protocol, and sampling are all correct. Only then start adding custom spans.

Implementation Patterns

Default auto-instrumentation is the baseline. For most apps you should not write tracing code at all for HTTP requests, Eloquent/DB queries, Redis calls, and queue jobs — enable the relevant groups in config and let the package produce spans. Treat manual instrumentation as an enhancement, not the primary strategy.

Custom spans around business logic. Resolve the OpenTelemetry tracer from the container rather than constructing it yourself, so you inherit the configured sampler/exporter:

$tracer = app(\OpenTelemetry\API\Trace\TracerInterface::class);

$span = $tracer->spanBuilder('checkout.process')
    ->setAttribute('cart.items', $cart->items()->count())
    ->startSpan();

try {
    // ... work
} finally {
    $span->end();
}

Always use try/finally (or the span->activate() scope pattern) — an unended span is silently dropped, and that is the single most common cause of "my custom span never appeared".

Propagate context across boundaries. For outbound HTTP calls, ensure the tracing headers are injected (Guzzle middleware or the package's client instrumentation). Without propagation, your downstream services start orphan traces and the trace view in Jaeger/Tempo fragments into unrelated trees.

Queue jobs. Jobs are a distinct trace root unless the dispatch carries the parent context. Two patterns matter day-to-day:

  • Verify the dispatch site links to the job execution span (producer/consumer link or parent-child), otherwise you cannot follow a request that fans out into async work.
  • Rely on the package to end and export the job span when the job finishes, not when the worker's process ends. 0.0.32 specifically fixes this: previously the monitoring/export for queue job spans was tied to worker shutdown, which meant long-running queue:work processes showed delayed or missing job spans, and a worker killed mid-life lost them entirely. If queue observability is a reason you adopted this package, treat 0.0.32 as the minimum version.

Sampling strategy per environment. parentbased_traceidratio in production with a low ratio (1–10%) and always_on locally. Parent-based sampling is what keeps a distributed trace coherent — head sampling without the parent-based wrapper will produce partial traces where a service drops a trace the caller kept.

Resource attributes for real triage. Set deployment.environment, service.version (inject from your build/CI), and the pod/host identifier via env. Without service.version you cannot correlate a latency regression to a release, which is the main reason teams open the tracing UI at all.

Instrument the console and scheduler too. Long-running queue workers and scheduled commands are where most silent failures live. Once enabled, a queue:work process and a scheduled command each produce their own trace, and you can alert on their duration/error rate.

Gotchas and Tips

This is 0.0.x — pin it. Every release is a potential breaking change to config keys or provider wiring. Commit the lock file, read the changelog before composer update, and prefer exact-ish constraints (0.0.32) over ^0.0.

config:cache will shadow your env changes. After editing OTEL_* variables, run php artisan config:clear (or re-cache). A stale cached config pointing at the old collector endpoint is a classic two-hour debug session.

Upgrading to 0.0.32 requires a worker restart. Because the fix changes when queue job spans are ended and exported, existing queue:work processes still hold the old behaviour in memory. Run php artisan queue:restart after deploy, or you will conclude the fix did nothing.

No traces at all? Check in this order: OTEL_SDK_DISABLED/the package's enabled flag → sampler ratio → endpoint and protocol mismatch (4317 = gRPC, 4318 = HTTP; mixing them fails silently) → collector reachability from inside the container (network policy, not localhost from a container) → batch exporter flush interval, which can make you think spans are missing when they are just buffered.

PII leaks through span attributes. db.statement and queue job payload attributes can capture SQL bindings, emails, and tokens. Disable binding capture or add a span processor that redacts before export — this is far cheaper to do now than after a compliance review.

Keep span names low-cardinality. GET /users/{id} not GET /users/1234, and never put a user ID or order ID in a span name. High-cardinality names blow up your tracing backend's index and the bill.

Overhead is real but tunable. If you see latency regressions after enabling, the usual culprits are DB statement capture, per-query span creation on chatty ORMs, and synchronous exporters. Use the batch exporter (default) and drop DB instrumentation in the highest-throughput services.

Extension points to reach for instead of forking: custom span processors (redaction, attribute enrichment), swapping the exporter, listening to the package's events where provided, and wrapping only your own service-layer entry points with manual spans. If you find yourself patching vendor code, that is a signal to open an upstream issue — the package is small and the maintainer is responsive, as 0.0.32 demonstrates.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib