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

Opentelemetry Symfony Laravel Package

traceway/opentelemetry-symfony

Symfony integration for OpenTelemetry that adds tracing and instrumentation to your applications. Capture HTTP requests, database queries, and service calls, then export spans via OTLP to observability backends such as Jaeger, Tempo, or Traceway for performance monitoring and debugging.

View on GitHub
Deep Wiki
Context7

Getting Started

Three steps to a first trace:

composer require traceway/opentelemetry-symfony
# production: also install the C extensions
pecl install protobuf grpc   # ext-protobuf strongly recommended; ext-grpc only for OTLP/gRPC

Register the bundle in config/bundles.php, then point it at a collector. The config root follows the package name — confirm the exact alias in the README, but it will look like:

# config/packages/traceway_opentelemetry.yaml
traceway_opentelemetry:
  service_name: my-app
  exporter:
    endpoint: '%env(OTEL_EXPORTER_OTLP_ENDPOINT)%'   # http://localhost:4318
    protocol: http/protobuf
  traces:
    sampler: parent_based
  metrics:
    enabled: false        # opt-in, leave off until you need it

Run an OTel Collector locally (or point straight at Jaeger/Tempo/Grafana Alloy) and hit one endpoint. Look for two things first: the service.name resource attribute is set (not unknown_service), and a single HTTP span exists for your route with child spans hanging off it.

First use case — the slow-endpoint waterfall. Pick your worst p95 route and open it in the trace UI. Because HTTP, Doctrine DBAL, Cache, Twig and outbound HTTP are all auto-instrumented, you get a breakdown for free: 400ms in five DB queries, 900ms in one Guzzle call, 60ms rendering product/show.html.twig. That is the whole value proposition on day one — no code changes, just a timeline. Note that as the Symfony-equivalent of dropping Telescope or Clockwork on a route, except it survives production and crosses service boundaries.

Implementation Patterns

Baseline before custom code. The bundle instruments the kernel, console, Messenger, Scheduler, Mailer, DBAL, Cache, Twig and outbound clients automatically. Spend the first day reading traces before writing any. Most teams discover half their latency is a cache stampede or an N+1 the profiler never surfaced.

Cross-service propagation is the killer feature. Outgoing calls through Symfony HttpClient, Guzzle and PSR-18 clients get a W3C traceparent injected by decorator, so a frontend → API → worker chain renders as one trace, provided the downstream service is also instrumented. Verify the header actually leaves:

# on the receiving side, or in a debug proxy
curl -s localhost:8080/api/foo -H 'traceparent: 00-<trace-id>-<span-id>-01'

Messenger: instrument the envelope, not the handler. The bundle creates producer spans at dispatch and consumer spans on handling, propagating context through message headers. This separates queue wait from processing time — the two numbers that matter when async work "feels slow". If you hand-roll a transport or a custom async boundary (Redis list, cron-triggered job), you must copy the traceparent header yourself or the trace breaks.

Custom spans for business logic. Inject the tracer and wrap the unit of work that matters — payment capture, PDF generation, a multi-step import:

$span = $this->tracer->spanBuilder('invoice.generate')->startSpan();
$span->setAttribute('invoice.id', $id);
try {
    // work
} catch (\Throwable $e) {
    $span->recordException($e);
    $span->setStatus(Status::STATUS_ERROR);
    throw $e;
} finally {
    $span->end();
}

Add high-value attributes once at the edge — user id, tenant, plan, feature flag — via a kernel listener or the bundle's request-attribute hook, then they appear on every child span's trace view. Resist adding attributes inside loops.

Log correlation. Enable the Monolog processor and formatter so trace_id/span_id land in every log line. This is the pattern that actually changes day-to-day debugging: error in Sentry or a log search → copy trace_id → open the full waterfall. Make sure the processor sits before the handler in the Monolog stack, and that JSON logs keep the fields as strings.

Metrics: opt in narrowly. Turn on metrics only for a handful of golden signals (request duration histogram, queue depth, cache hit ratio). Cardinality is the failure mode — never label by user id, URL with IDs, or SQL text.

Environment split. Dev: console or simple exporter, always_on sampler, full DB statements. Prod: OTLP batch exporter, parent_based + ratio sampler, redacted attributes, health-check routes excluded. Keep one source of truth — either Symfony config or OTEL_* env vars, not both, because the precedence will bite you.

Testing. Disable export in test env (OTEL_SDK_DISABLED=true) so CI isn't noisy, but add a couple of tests against an in-memory exporter to assert a span name and key attribute exist for critical flows.

Laravel-developer translation: a bundle is a package plus a service provider; config/packages/*.yaml is config/*.php; the decorators this bundle registers are the container bindings you'd normally write by hand in a service provider. The mental model transfers cleanly.

Gotchas and Tips

  • Long-running workers are the #1 trap. messenger:consume never fires kernel.terminate per message, so a batch span processor may hold spans indefinitely. Set OTEL_BSP_SCHEDULE_DELAY, cap OTEL_BSP_MAX_QUEUE_SIZE, and restart workers with --time-limit in production. Dropped spans in workers almost always mean the queue filled.
  • Double instrumentation. If Guzzle is called through Symfony HttpClient, or your cache is Redis with a client-level instrumentor installed, you get duplicate or nested client spans. Disable one side explicitly.
  • Cardinality in span names. Twig spans named after templates and HTTP spans named after raw URLs both explode your backend's index. Use route names and template groups; strip IDs.
  • PII leaks silently. db.statement can include bound values, and HTTP client spans can capture headers. Redact authorization, cookie, and any request/response bodies before this reaches a shared collector. Check what a "safe" query actually looks like in the UI, not in the config.
  • Sampling confusion. With parent_based, an upstream service sending sampled=0 makes your service silently produce nothing. When traces "disappear" in a multi-service setup, force always_on briefly to isolate whether it's the sampler or the exporter.
  • Exporter failures are quiet by design. Wrong port (4317 gRPC vs 4318 HTTP), missing TLS, or a stopped collector produces a working app with no traces. Always confirm against collector logs, not app logs.
  • Sub-requests and fragments. ESI, Twig fragments and the profiler toolbar create extra server spans. Exclude /_fragment, /_profiler, /_wdt and health checks or your traces are permanently noisy.
  • Console noise. Doctrine migrations and cache warmup commands flood the backend. Disable console tracing in those environments, or filter by command name.
  • Set resource attributes. service.name, service.version and deployment.environment are what make traces filterable. Without them you get unknown_service and can't tell staging from prod.
  • Extension points. Where the automatic span shape doesn't fit (custom clients, third-party SDKs, legacy queue), the answer is almost always "write the span yourself at the boundary and propagate context manually" — the bundle covers Symfony's own seams, not the whole world.
  • Pin your versions. The OTel PHP API/SDK is stable, but contrib/exporter packages and Symfony 6.4→7.x support matrices move. Lock minor versions and read the changelog on upgrade; a broken exporter decorator is invisible until you notice the silence.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route