PHPackages                             traceway/opentelemetry-symfony - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. traceway/opentelemetry-symfony

ActiveSymfony-bundle[Database &amp; ORM](/categories/database)

traceway/opentelemetry-symfony
==============================

Pure-PHP OpenTelemetry instrumentation for Symfony — automatic HTTP, Console, HttpClient, Messenger, Doctrine DBAL, Cache, Twig tracing and Monolog log-trace correlation with response propagation, a lightweight Tracing helper, route templates, and semantic conventions. No C extension required (ext-protobuf recommended for production).

v2.1.0(3w ago)724.6k4[4 issues](https://github.com/tracewayapp/opentelemetry-symfony-bundle/issues)[4 PRs](https://github.com/tracewayapp/opentelemetry-symfony-bundle/pulls)MITPHPPHP &gt;=8.1CI passing

Since Mar 14Pushed 1mo ago2 watchersCompare

[ Source](https://github.com/tracewayapp/opentelemetry-symfony-bundle)[ Packagist](https://packagist.org/packages/traceway/opentelemetry-symfony)[ Docs](https://github.com/tracewayapp/opentelemetry-symfony-bundle)[ RSS](/packages/traceway-opentelemetry-symfony/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (10)Dependencies (66)Versions (44)Used By (0)

 [    ![Traceway Logo](https://raw.githubusercontent.com/tracewayapp/traceway/main/Traceway%20Logo.png)  ](https://tracewayapp.com)

OpenTelemetry Symfony Bundle
============================

[](#opentelemetry-symfony-bundle)

[![CI](https://github.com/tracewayapp/opentelemetry-symfony-bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/tracewayapp/opentelemetry-symfony-bundle/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/578e9ab0ca4d7c6d0b5a25073184b143400f1f8c16acc42f275412cf36aa5c1f/68747470733a2f2f636f6465636f762e696f2f67682f74726163657761796170702f6f70656e74656c656d657472792d73796d666f6e792d62756e646c652f67726170682f62616467652e737667)](https://codecov.io/gh/tracewayapp/opentelemetry-symfony-bundle)[![Packagist Version](https://camo.githubusercontent.com/aebf2e633114969dc5b25a55556150c1f2ec1a81e483648e5eedde16e336c12d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74726163657761792f6f70656e74656c656d657472792d73796d666f6e792e737667)](https://packagist.org/packages/traceway/opentelemetry-symfony)[![Packagist Downloads](https://camo.githubusercontent.com/440fb495cfbe28a94beae249d1036421f87f938080648c1cf2b63b16cd08172c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74726163657761792f6f70656e74656c656d657472792d73796d666f6e792e737667)](https://packagist.org/packages/traceway/opentelemetry-symfony)[![PHP Version](https://camo.githubusercontent.com/04744bae0a61d2ffe29c26f07a9612eae20445fc6feaeb77b3af1f0e9be6447c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e312d3838393242462e737667)](https://php.net)[![Symfony Version](https://camo.githubusercontent.com/df69560fe6c25fd0268689a4bbba1950f74df1ee59a1b5d6f744f96d63043647/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73796d666f6e792d253345253344362e342d3030303030302e737667)](https://symfony.com)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![Discord](https://camo.githubusercontent.com/397741123d69503a0a224452a3629154669e9a870686f0c21c68527c7d8faa07/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446973636f72642d4a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465)](https://discord.gg/9tPn2SB3)

Pure-PHP OpenTelemetry instrumentation for Symfony — automatic tracing for HTTP, Console, HttpClient, Messenger, Mailer, Scheduler, Doctrine DBAL, Cache, and Twig, plus Monolog log-trace correlation, OpenTelemetry log export, and opt-in metrics for Messenger, DBAL, HTTP server/client, and Mailer. No C extension required.

Works with any OpenTelemetry-compatible backend: [Traceway](https://tracewayapp.com), [Jaeger](https://www.jaegertracing.io/), [Zipkin](https://zipkin.io/), [Datadog](https://www.datadoghq.com/), [Grafana Tempo](https://grafana.com/oss/tempo/), [Honeycomb](https://www.honeycomb.io/), and more.

- **Pure PHP** — no C extension required; installs on every managed Symfony host
- **Production-ready** — stable since v1.0, PHPStan level 10 with no baseline, supports Symfony 6.4 LTS through 8.x
- **Correct under load** — Messenger trace context propagates across async queue boundaries, Doctrine DBAL 3 and 4 both CI-tested, re-entrance guards prevent export-path recursion in HttpClient and the log handler

Quick Start
-----------

[](#quick-start)

```
composer require traceway/opentelemetry-symfony
```

```
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=my-symfony-app
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
# Optional: OTEL_RESOURCE_ATTRIBUTES=service.version=1.0
```

> Use `http/json` unless you have `ext-protobuf` installed — see [Performance](#performance).

With Symfony Flex the bundle auto-registers; without Flex, add `Traceway\OpenTelemetryBundle\OpenTelemetryBundle::class => ['all' => true]` to `config/bundles.php`.

That's it. Every HTTP request, console command, outgoing call, Messenger job, DB query, cache operation, and Twig render is now traced.

What Gets Traced
----------------

[](#what-gets-traced)

ComponentSpan KindWhat's captured**HTTP requests**SERVERRoute templates (`GET /api/items/{id}`), status codes, body sizes, client IP, exceptions, sub-requests**Console commands**SERVERCommand name, arguments, exit code, exceptions**HttpClient**CLIENTOutgoing requests with W3C context propagation, OTLP endpoint auto-excluded, re-entrance guard**Messenger**PRODUCER/CONSUMERMessage class, transport, W3C context propagation across async boundaries**Scheduler**CONSUMERPer scheduled-task run: schedule name, trigger expression, next-run, cancellation marker. Requires `symfony/scheduler`. Messenger spans for scheduled envelopes are suppressed automatically**Mailer**PRODUCER + CLIENTTwo-span split: PRODUCER on `MailerInterface::send` and CLIENT on the transport. Recipient count, message-id, `X-Transport` routing. Subject opt-in. Requires `symfony/mailer`**Doctrine DBAL**CLIENTSQL queries (parameterized), transactions, db system/namespace auto-detection. **DBAL 3.6+ and 4.x both CI-tested****Cache**INTERNAL`get` (hit/miss), `delete`, `invalidateTags` with pool name. Requires `symfony/cache`**Twig**INTERNALTemplate name, nested includes. Requires `twig/twig`**Monolog**—Inject `trace_id` + `span_id` into every log record (`monolog/monolog`). Opt-in OTel Logs API export with per-channel instrumentation scope (`symfony/monolog-bundle`, **off by default**)Also: Server-Timing response headers, full [OTel semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/).

Configuration
-------------

[](#configuration)

All options are optional — the bundle works out of the box with zero configuration. Create `config/packages/open_telemetry.yaml` to customize:

```
open_telemetry:
    traces:
        enabled: true
        tracer_name: 'opentelemetry-symfony'

        excluded_paths: [/health, /_profiler, /_wdt]
        record_client_ip: true                # disable for GDPR
        error_status_threshold: 500           # 400-599

        console:
            enabled: true
            excluded_commands: [cache:clear, assets:install]

        http_client:
            enabled: true
            excluded_hosts: []                # OTLP endpoint is auto-excluded

        messenger:
            enabled: true
            root_spans: false                 # true = standalone traces per consumed message

        scheduler:
            enabled: true                     # suppresses parallel Messenger spans for scheduled tasks

        mailer:
            enabled: true
            record_subject: false             # subjects can be PII

        doctrine:
            enabled: true
            record_statements: true           # false = hide SQL from spans

        cache:
            enabled: true
            excluded_pools: [cache.system, cache.validator, cache.serializer]

        twig:
            enabled: true
            excluded_templates: ['@WebProfiler/', '@Debug/']

    metrics:
        enabled: false
        meter_name: 'opentelemetry-symfony'

        messenger:
            enabled: false
            excluded_queues: []

        doctrine:
            enabled: false

        http_server:
            enabled: false
            excluded_paths: []                # same prefix-match rules as tracing excluded_paths

        http_client:
            enabled: false
            excluded_hosts: []                # OTLP endpoint is auto-excluded

        mailer:
            enabled: false

    logs:
        correlation:
            enabled: true                     # inject trace_id/span_id into log records

        export:
            enabled: false                    # OTel Logs API export (requires symfony/monolog-bundle)
            level: debug
            capture_code_attributes: false    # fallback debug_backtrace when IntrospectionProcessor is absent
            unprefixed_attributes: true       # flat context/extra attributes (matches Java/Python/.NET/JS)
```

> Upgrading from v1.x? See [UPGRADE-2.0.md](UPGRADE-2.0.md) for the flat→nested mapping and migration notes.

### Environment Variables

[](#environment-variables)

VariableExampleDescription`OTEL_PHP_AUTOLOAD_ENABLED``true`Enable SDK auto-initialization`OTEL_SERVICE_NAME``my-symfony-app`Service name shown in your backend`OTEL_TRACES_EXPORTER``otlp`Traces exporter (`otlp`, `zipkin`, `console`, `none`)`OTEL_LOGS_EXPORTER``otlp`Logs exporter (`otlp`, `console`, `none`) — only used when `logs.export.enabled: true``OTEL_EXPORTER_OTLP_ENDPOINT``http://localhost:4318`Collector/backend endpoint`OTEL_EXPORTER_OTLP_PROTOCOL``http/json`Protocol (`http/json`, `http/protobuf`, `grpc`)See the [OpenTelemetry SDK docs](https://opentelemetry.io/docs/languages/php/exporters/) for all available options.

Manual Instrumentation
----------------------

[](#manual-instrumentation)

Inject `TracingInterface` for one-liner span creation:

```
use Traceway\OpenTelemetryBundle\TracingInterface;

class OrderService
{
    public function __construct(private readonly TracingInterface $tracing) {}

    public function process(int $orderId): void
    {
        $this->tracing->trace('order.validate', function () use ($orderId) {
            // validation logic...
        });

        $this->tracing->trace('order.fulfill', function () {
            $this->tracing->trace('inventory.reserve', fn () => $this->reserve());
            $this->tracing->trace('payment.charge', fn () => $this->charge());
        });
    }
}
```

Mock in tests with `$this->createStub(TracingInterface::class)` and have `trace()` invoke the callback directly.

Metrics
-------

[](#metrics)

**Off by default.** Enable to export OpenTelemetry metrics alongside traces, with opt-in automatic instrumentation for Messenger, Doctrine DBAL, HTTP server/client, and Mailer.

```
open_telemetry:
    metrics:
        enabled: true
        meter_name: 'opentelemetry-symfony'
        messenger:
            enabled: true
            excluded_queues: []
        doctrine:
            enabled: true
```

### What Gets Measured

[](#what-gets-measured)

InstrumentKindUnitSourceAttributes`messaging.process.duration`Histogram`s`Messenger consume`messaging.system`, `messaging.operation.name`, `messaging.operation.type`, `messaging.destination.name`, `error.type` on failure`messaging.client.consumed.messages`Counter`{message}`Messenger consumeSame as above`messaging.client.operation.duration`Histogram`s`Messenger dispatchSame shape, `messaging.operation.{name,type}` = `send`, destination derived from `SentStamp::getSenderAlias()` (falls back to sender FQCN)`messaging.client.sent.messages`Counter`{message}`Messenger dispatchSame as above`db.client.operation.duration`Histogram`s`DBAL connection`db.system.name`, `db.namespace`, `server.address`, `server.port`, `db.operation.name`, `db.collection.name` (when extractable), `error.type` on failure`http.server.request.duration`Histogram`s`HTTP server`http.request.method`, `url.scheme`, `http.route` if matched, `http.response.status_code`, `server.address`, `server.port`, `error.type` on failure`http.server.active_requests`UpDownCounter`{request}`HTTP server`http.request.method`, `url.scheme`, `server.address`, `server.port``http.server.request.body.size`Histogram`By`HTTP serverSame as duration (emitted when `Content-Length` is set)`http.server.response.body.size`Histogram`By`HTTP serverSame as duration (emitted when `Content-Length` is set)Names and attributes follow OTel semantic conventions ([messaging](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-metrics/), [database](https://opentelemetry.io/docs/specs/semconv/database/database-metrics/), [HTTP](https://opentelemetry.io/docs/specs/semconv/http/http-metrics/)). `http.server.request.duration` and `error.type` are Stable; the rest are Development.

- **HTTP server** — only main requests are measured; sub-requests are covered by the main duration. Service identity comes from the OTel resource (`OTEL_SERVICE_NAME`, `OTEL_RESOURCE_ATTRIBUTES`), not from metric name prefixing.
- **Messenger** — `excluded_queues` matches the transport name on both sides (`ReceivedStamp::getTransportName()` on consume, `SentStamp::getSenderAlias()` on dispatch). A dispatched envelope landing on multiple transports emits one point per non-excluded transport.
- **DBAL** — records duration for `Connection::query()`/`exec()`, prepared `Statement::execute()`, and transaction control methods. SQL text is **never** recorded — only the leading keyword (`db.operation.name`) and the primary table when extractable (`db.collection.name`).

**HTTP Client** (outgoing requests):

InstrumentKindUnitStabilityAttributes`http.client.request.duration`Histogram`s`**Stable**`http.request.method`, `server.address`, `server.port`, `url.scheme`, `http.response.status_code` on response, `error.type` on transport failure`http.client.request.body.size`Histogram`By`DevelopmentSame as duration (emitted when `Content-Length` header or a string body is present)`http.client.response.body.size`Histogram`By`DevelopmentSame as duration (emitted when response `Content-Length` is set or the body is fully read)`http_client.excluded_hosts` skips matching hostnames; the OTLP endpoint (from `OTEL_EXPORTER_OTLP_ENDPOINT`) is always auto-excluded to prevent instrumentation loops.

**Mailer** (outbound transport sends):

InstrumentKindUnitStabilityAttributes`messaging.client.operation.duration`Histogram`s`Development`messaging.system=symfony_mailer`, `messaging.operation.name=send`, `messaging.operation.type=send`, `messaging.destination.name` from `X-Transport` header when present, `error.type` on failure`messaging.client.sent.messages`Counter`{message}`DevelopmentSame as durationDecoration sits inside `TraceableTransports` so metric points record while the trace span is still active — backends that support exemplars can link directly from a metric data point to the corresponding trace.

### Manual Metrics

[](#manual-metrics)

Inject `MeterRegistryInterface` to record your own counters, histograms, and up/down counters without touching the `MeterProvider` directly:

```
use OpenTelemetry\API\Metrics\CounterInterface;
use Traceway\OpenTelemetryBundle\Metrics\MeterRegistryInterface;

final class MediaDownloader
{
    private readonly CounterInterface $downloads;

    public function __construct(MeterRegistryInterface $metrics)
    {
        $this->downloads = $metrics->counter(
            'media.download.count',
            description: 'Media downloads by outcome',
        );
    }

    public function download(string $url): void
    {
        try {
            // ... download logic
            $this->downloads->add(1, ['outcome' => 'success']);
        } catch (\Throwable $e) {
            $type = $e::class;
            if (str_contains($type, '@anonymous')) {
                $type = get_parent_class($e) ?: \Throwable::class;
            }
            $this->downloads->add(1, ['outcome' => 'error', 'error.type' => $type]);
            throw $e;
        }
    }
}
```

The registry caches instruments per name, so repeated `->counter('x')` calls return the same instance. When the OTel SDK is not configured, the NoOp meter provider returns no-op instruments — safe to inject unconditionally. The `@anonymous` guard above normalises anonymous-class names to their parent; otherwise `$e::class` embeds a filesystem path, leaking code locations and exploding label cardinality.

### Metrics Environment Variables

[](#metrics-environment-variables)

VariableExampleDescription`OTEL_METRICS_EXPORTER``otlp`Metrics exporter (`otlp`, `console`, `none`) — only used when `metrics.enabled: true``OTEL_EXPORTER_OTLP_METRICS_ENDPOINT``http://localhost:4318/v1/metrics`Override the generic `OTEL_EXPORTER_OTLP_ENDPOINT` for metricsPerformance
-----------

[](#performance)

Near-zero overhead when the SDK is inactive — every component short-circuits via `isEnabled()`. When tracing is on, almost all cost is in span export, not instrumentation. PHP-FPM has no background thread, so `BatchSpanProcessor` flushes during request shutdown.

**Use `http/json` unless you have `ext-protobuf` installed.** PHP's native `json_encode()` is faster than the pure-PHP protobuf encoder, which adds significant CPU overhead under load. Switch to `http/protobuf` only with the C extension installed.

For high-traffic apps:

- Run a local OTel Collector at `localhost:4318` (sub-ms latency) and let it forward asynchronously.
- Enable head sampling: `OTEL_TRACES_SAMPLER=parentbased_traceidratio` + `OTEL_TRACES_SAMPLER_ARG=0.1`.
- Use `traces.excluded_paths` / `traces.cache.excluded_pools` to drop noisy spans.

Contributing
------------

[](#contributing)

```
git clone https://github.com/tracewayapp/opentelemetry-symfony-bundle.git
cd opentelemetry-symfony-bundle
composer install
vendor/bin/phpunit
vendor/bin/phpstan analyse
```

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

53

—

FairBetter than 96% of packages

Maintenance92

Actively maintained with recent releases

Popularity39

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 72.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

22

Last Release

26d ago

Major Versions

v1.9.0 → v2.0.02026-05-19

### Community

Maintainers

![](https://www.gravatar.com/avatar/8160951a0cd8e658add59bc36c59497d410ac3a82ab800aa6cd40b54f188f287?d=identicon)[graphene](/maintainers/graphene)

---

Top Contributors

[![jstojiljkovic](https://avatars.githubusercontent.com/u/22980168?v=4)](https://github.com/jstojiljkovic "jstojiljkovic (121 commits)")[![srekcud](https://avatars.githubusercontent.com/u/12445827?v=4)](https://github.com/srekcud "srekcud (37 commits)")[![AndreasA](https://avatars.githubusercontent.com/u/1383094?v=4)](https://github.com/AndreasA "AndreasA (6 commits)")[![dusanstanojeviccs](https://avatars.githubusercontent.com/u/15340338?v=4)](https://github.com/dusanstanojeviccs "dusanstanojeviccs (2 commits)")

---

Tags

instrumentationobservabilityopentelemetryotelphpsymfonysymfony-bundletracingconsolesymfonyloggingtwigdoctrinetracingopentelemetryotelcachemonologobservabilityinstrumentation

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/traceway-opentelemetry-symfony/health.svg)

```
[![Health](https://phpackages.com/badges/traceway-opentelemetry-symfony/health.svg)](https://phpackages.com/packages/traceway-opentelemetry-symfony)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.5M374](/packages/easycorp-easyadmin-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M196](/packages/sulu-sulu)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

9317.2k55](/packages/open-dxp-opendxp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
