Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tracewayapp/opentelemetry-symfony-bundle/llms.txt

Use this file to discover all available pages before exploring further.

The bundle has near-zero overhead when the SDK is inactive — every component short-circuits via isEnabled() on the first call. When tracing is on, almost all measurable cost sits in span export, not in instrumentation itself. The strategies below let you control exactly how much export work each request or worker cycle pays for.

Protocol selection

Choosing the right OTLP protocol is the fastest single change you can make.

http/json (recommended)

PHP’s native json_encode() is faster than the pure-PHP protobuf encoder under load. Use this unless you have ext-protobuf installed.

http/protobuf + ext-protobuf

The C extension serializes protobuf at native speed. Safe to use and smaller on the wire, but requires the extension.

grpc (avoid in FPM)

gRPC requires a persistent background thread. PHP-FPM has no such thread — avoid grpc in FPM environments.
# Default — fastest serialization without an extension
OTEL_EXPORTER_OTLP_PROTOCOL=http/json

# Use protobuf only when ext-protobuf is installed
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Do not set OTEL_EXPORTER_OTLP_PROTOCOL=grpc in PHP-FPM. gRPC relies on a persistent connection managed by a background thread that FPM workers do not have. The exporter will fail or block at request shutdown.

BatchSpanProcessor and PHP-FPM

PHP-FPM workers have no background thread. BatchSpanProcessor accumulates spans during the request and flushes them synchronously during the kernel.terminate phase — after the response has been sent to the browser, but still within the same process lifecycle. The practical consequence is that export latency is paid at the tail of every request rather than amortized in the background. The best mitigation is a local OTel Collector sidecar:
# Point the exporter at a local Collector running on the same host
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
A Collector at localhost:4318 accepts spans over loopback (sub-millisecond), buffers and batches them, then forwards to your backend asynchronously. The Symfony worker exits in microseconds instead of waiting for a round trip to a remote endpoint.
Run the OTel Collector as a sidecar container or a systemd service co-located with your PHP-FPM pool. Even a minimal Collector config (OTLP receiver → OTLP exporter) cuts tail latency dramatically compared to exporting directly to a remote backend.

Head sampling

Sampling at the trace head reduces export volume before any spans are created. The parentbased_traceidratio sampler respects the sampling decision propagated by an upstream service (so distributed traces stay consistent) and falls back to ratio-based sampling for traces that originate here.
# Sample 10% of all new traces; respect upstream sampling decisions
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1

Reducing noisy spans

Some routes and cache pools generate high span volumes with low diagnostic value. Filter them out at the source so they never enter the export pipeline.

Excluded paths (traces and metrics)

open_telemetry:
    traces:
        excluded_paths:
            - /health
            - /_profiler
            - /_wdt

    metrics:
        http_server:
            excluded_paths:
                - /health
                - /_profiler

Excluded cache pools

open_telemetry:
    traces:
        cache:
            excluded_pools:
                - cache.system
                - cache.validator
The cache.system and cache.validator pools are accessed on almost every request by Symfony’s internals. Excluding them typically cuts cache span volume by 80–90% without losing application-level observability.

Excluded HTTP client hosts

open_telemetry:
    traces:
        http_client:
            excluded_hosts:
                - localhost
                - monitoring.internal
Use excluded_hosts to suppress spans for calls to your OTel Collector, internal health endpoints, or monitoring agents that would otherwise appear as client spans on every request.
traces.http_client.excluded_hosts accepts bare hostnames. The OTLP exporter endpoint is automatically excluded — you do not need to add it manually.

Long-running processes

Messenger workers, FrankenPHP, RoadRunner, and Swoole keep a single PHP process alive across thousands of request or message cycles. Without proper reset, cached tracer and meter state from one cycle can bleed into the next. Every stateful service in the bundle implements Symfony’s ResetInterface. When Symfony fires kernel.reset between worker cycles, the services_resetter clears all cached tracer and meter instances so each cycle starts with a fresh, clean slate.
# Messenger worker — reset fires automatically between message cycles
bin/console messenger:consume async --limit=100
No additional configuration is needed. As long as your worker uses Symfony’s standard process model (which calls kernel.reset between cycles), tracer and meter state is isolated per cycle. This applies to Messenger workers, FrankenPHP, RoadRunner, and Swoole when using the Symfony Runtime component.

Summary

High-traffic checklist:
  1. Set OTEL_EXPORTER_OTLP_PROTOCOL=http/json (or http/protobuf with ext-protobuf) — avoid grpc in FPM.
  2. Run a local OTel Collector at localhost:4318 to absorb export latency inside the process.
  3. Enable head sampling with OTEL_TRACES_SAMPLER=parentbased_traceidratio and set OTEL_TRACES_SAMPLER_ARG to your target rate (e.g. 0.1 for 10%).
  4. Add health-check and profiler paths to traces.excluded_paths and metrics.http_server.excluded_paths.
  5. Add cache.system and cache.validator to traces.cache.excluded_pools.

Build docs developers (and LLMs) love