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.
Once the bundle is registered and the OpenTelemetry environment variables are configured, every listed component is instrumented automatically — no code changes, annotations, or decorators needed. The bundle hooks into Symfony’s event system, Messenger middleware pipeline, and decorator pattern to wrap each integration with properly named spans, semantic-convention attributes, and context propagation.
All instrumentation is enabled by default. You can disable or tune individual integrations through config/packages/open_telemetry.yaml without touching application code.
HTTP Requests
Symfony’s HttpKernel events drive one span per HTTP request, including sub-requests.
| Detail | Value |
|---|
| Span kind | SERVER (main requests) · INTERNAL (sub-requests) |
| Span name | HTTP METHOD initially, then updated to route template, e.g. GET /api/items/{id} |
| Attributes | http.request.method, url.full, url.path, url.scheme, url.query, server.address, server.port, network.protocol.version, user_agent.original, client.address (opt-out), http.response.status_code, http.request.body.size, http.response.body.size |
| Error recording | Exceptions are recorded on the span; status codes ≥ threshold (500 default) set STATUS_ERROR |
| Context extraction | W3C traceparent/tracestate headers are extracted from the incoming request to continue a distributed trace |
Relevant config keys:
open_telemetry:
traces:
excluded_paths: [/health, /_profiler, /_wdt]
record_client_ip: true # set false to comply with GDPR
error_status_threshold: 500 # 400–599; responses below this threshold are not errors
Route parameters are substituted back to placeholders (e.g. /api/items/42 → /api/items/{id}) using longest-match replacement, so trace backends correctly group high-cardinality endpoints.
Console Commands
Every Symfony console command dispatch is wrapped in a SERVER span.
| Detail | Value |
|---|
| Span kind | SERVER |
| Span name | Command name (e.g. app:send-emails) |
| Attributes | Command name, arguments, exit code, exception details on failure |
Relevant config keys:
open_telemetry:
traces:
console:
enabled: true
excluded_commands: [cache:clear, assets:install]
HttpClient (Outgoing Requests)
TraceableHttpClient decorates HttpClientInterface to add CLIENT spans and inject W3C propagation headers on every outgoing call.
| Detail | Value |
|---|
| Span kind | CLIENT |
| Span name | METHOD hostname (e.g. GET api.example.com) |
| Attributes | http.request.method, url.full, url.path, url.scheme, server.address, server.port, http.response.status_code |
| Context injection | traceparent / tracestate headers are injected into every outgoing request |
Special behaviors:
- OTLP endpoint auto-exclusion — any URL that starts with the value of
OTEL_EXPORTER_OTLP_ENDPOINT is always skipped, preventing instrumentation loops where the exporter itself triggers a new span.
- Re-entrance guard — if an outgoing HTTP call is triggered while another traced call is already in flight (e.g. the OTLP exporter uses this same client), the nested call is passed through without creating a span.
excluded_hosts — additional hostnames to skip, matched case-insensitively.
Relevant config keys:
open_telemetry:
traces:
http_client:
enabled: true
excluded_hosts: [internal-service.local]
Disabling the OTLP endpoint auto-exclusion is not possible through config — it is always enforced to prevent infinite export loops.
Messenger
The OpenTelemetryMiddleware creates spans on both the dispatch and consume sides, carrying trace context across async transport boundaries with TraceContextStamp.
| Detail | Value |
|---|
| Span kind (dispatch) | PRODUCER |
| Span kind (consume) | CONSUMER |
| Span name | Short message class name + operation, e.g. ProcessOrder publish / ProcessOrder process |
| Attributes | messaging.system=symfony_messenger, messaging.operation.type, messaging.message.class, messaging.destination.name (transport name on consume) |
| W3C propagation | traceparent/tracestate are serialised into a TraceContextStamp on dispatch and extracted on consume, linking async worker spans to their originating trace |
root_spans mode — when enabled, consumed messages create root spans with no parent, so task-oriented backends (Traceway, Sentry) classify each job as an independent task rather than a child of the dispatch trace.
Relevant config keys:
open_telemetry:
traces:
messenger:
enabled: true
root_spans: false # true = standalone traces per consumed message
Scheduler
The Scheduler integration instruments symfony/scheduler tasks via dedicated events, suppressing the parallel Messenger span that would otherwise be created for the same message.
| Detail | Value |
|---|
| Span kind | CONSUMER |
| Attributes | Schedule name, trigger description, next-run timestamp, cancellation marker |
Relevant config keys:
open_telemetry:
traces:
scheduler:
enabled: true # automatically suppresses duplicate Messenger spans for scheduled tasks
Requires symfony/scheduler. When scheduler.enabled: true, the Messenger middleware is automatically told to skip envelopes carrying ScheduledStamp so the richer scheduler span owns the observability without duplicate spans appearing.
Mailer
Mailer instrumentation uses a two-span split: one span at MailerInterface::send and a child span at the transport layer, giving you both the logical send and the actual transport call.
| Detail | Value |
|---|
| Span kinds | PRODUCER (logical send) + CLIENT (transport) |
| Attributes | Recipient count, message-id, X-Transport routing header, subject (opt-in) |
Relevant config keys:
open_telemetry:
traces:
mailer:
enabled: true
record_subject: false # subjects may contain PII — disabled by default
Doctrine DBAL
Every Connection::query(), Connection::exec(), prepared Statement::execute(), and transaction control call is wrapped in a CLIENT span. DBAL 3.6+ and 4.x are both CI-tested.
| Detail | Value |
|---|
| Span kind | CLIENT |
| Attributes | db.system.name, db.namespace, server.address, server.port, db.operation.name (leading SQL keyword), db.collection.name (primary table when extractable) |
SQL text (the full query string) is never recorded in span attributes by default. Set record_statements: false to hide even parameterised SQL from span names if required.
Relevant config keys:
open_telemetry:
traces:
doctrine:
enabled: true
record_statements: true # false = hide SQL from span names entirely
Cache
Cache pool operations are instrumented as INTERNAL spans, capturing hit/miss outcomes. Requires symfony/cache.
| Detail | Value |
|---|
| Span kind | INTERNAL |
| Operations | get (with hit/miss flag), delete, invalidateTags |
| Attributes | Pool name, cache key, hit/miss result |
Relevant config keys:
open_telemetry:
traces:
cache:
enabled: true
excluded_pools: [cache.system, cache.validator, cache.serializer]
Twig
Template rendering is captured as INTERNAL spans, including nested {% include %} and {% embed %} calls. Requires twig/twig.
| Detail | Value |
|---|
| Span kind | INTERNAL |
| Attributes | Template name |
Relevant config keys:
open_telemetry:
traces:
twig:
enabled: true
excluded_templates: ['@WebProfiler/', '@Debug/']
Monolog Log-Trace Correlation
The bundle automatically wires log-trace correlation through TraceContextProcessor, a standard Monolog ProcessorInterface that injects the active trace_id and span_id into the extra array of every log record. If no span is active (e.g. a log emitted outside a request), the record is passed through unchanged.
| Detail | Value |
|---|
| Span kind | — (not a span; injects into log records) |
| Fields injected | extra.trace_id, extra.span_id |
| Behavior | Injected on every Monolog record during an active span; no-op otherwise |
These values are forwarded by any existing Monolog handler (file, Elasticsearch, Datadog, etc.) — no OTel export infrastructure is required for correlation.
Relevant config keys:
open_telemetry:
logs:
correlation:
enabled: true # default — inject trace_id/span_id into every log record
export:
enabled: false # opt-in: forward records to OTel Logs API backend
OTel log export (forwarding Monolog records to an OpenTelemetry-compatible log backend via OTEL_LOGS_EXPORTER) is separate from correlation and is disabled by default. See the Logs page for the full export configuration.
The bundle automatically injects a Server-Timing response header on every traced HTTP request, reporting the trace ID so browser DevTools can correlate network timings with backend traces — no extra configuration required.
OTel Semantic Conventions
All attribute names follow the OpenTelemetry Semantic Conventions — http.*, url.*, server.*, db.*, messaging.*, code.*. This means spans are immediately interpretable by any OTel-compatible backend without custom mapping.
To disable a specific instrumentation, set its enabled key to false. For example, to turn off HttpClient tracing:open_telemetry:
traces:
http_client:
enabled: false
Each component can be individually enabled or disabled without affecting the others.