Skip to content

Distributed Tracing and Telemetry with OpenTelemetry

Winter Boot integrates with OpenTelemetry (OTel) for distributed traces, metrics, and logs. The integration is an optional module (OpenTelemetryModule), so it costs nothing when disabled. Once enabled, you can trace every web request with an interceptor, trace or count individual methods with attributes, and inject telemetry beans anywhere.

  1. Install the PHP OpenTelemetry extension

    Terminal window
    pecl install opentelemetry
    php.ini
    extension=opentelemetry.so
  2. Install the OpenTelemetry SDK and an exporter

    Terminal window
    composer require open-telemetry/sdk open-telemetry/exporter-otlp
  3. Enable the module in application.yml

    application.yml
    modules:
    - module: 'dev\winterframework\telemetry\OpenTelemetryModule'
    enabled: true
    configFile: opentelemetry.yml # required; looked up in the config directory
    config/opentelemetry.yml
    winter:
    telemetry:
    serviceName: my-winter-service

    To switch telemetry off without removing the module (for example in tests), set winter.telemetry.enabled: false.

  4. Configure the exporter with OTel environment variables

    Winter Boot uses the SDK’s global providers, so the service name, exporter, endpoint, and sampler are set with the standard OpenTelemetry environment variables:

    Terminal window
    export OTEL_PHP_AUTOLOAD_ENABLED=true
    export OTEL_SERVICE_NAME=my-winter-service
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_METRICS_EXPORTER=otlp
    export OTEL_LOGS_EXPORTER=otlp
    export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
    export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
    export OTEL_TRACES_SAMPLER=parentbased_always_on

    Without OTEL_PHP_AUTOLOAD_ENABLED=true, the SDK stays in no-op mode and nothing is exported.

Register OpenTelemetryWebInterceptor in your WebMvcConfigurer (see Interceptors) to create a span for every incoming HTTP request. The interceptor extracts the W3C Trace Context from request headers (enabling distributed trace propagation from upstream services), records the HTTP method, URL, and response status code, and closes the span — including exception recording — after the response is sent.

MyWebConfigurer.php
use dev\winterframework\telemetry\web\OpenTelemetryWebInterceptor;
use dev\winterframework\stereotype\Configuration;
use dev\winterframework\core\web\config\WebMvcConfigurer;
use dev\winterframework\core\web\config\InterceptorRegistry;
#[Configuration(name: 'webMvcConfigurer')]
class MyWebConfigurer implements WebMvcConfigurer
{
public function addInterceptors(InterceptorRegistry $registry): void
{
// Trace all incoming HTTP requests
$registry->addInterceptor(new OpenTelemetryWebInterceptor(), '.*');
}
}

Annotate any public bean method with #[Traceable] to have Winter Boot automatically create an OTel span around that method’s execution. The span is named after the method by default; override the name via the name argument. Exceptions are recorded on the span and the span status is set to ERROR automatically.

OrderService.php
use dev\winterframework\telemetry\stereotype\Traceable;
use dev\winterframework\stereotype\Service;
#[Service]
class OrderService
{
#[Traceable]
public function processOrder(int $orderId): void
{
// Execution is wrapped in an OTel span named after this method.
}
#[Traceable(name: 'charge-customer')]
public function chargeCustomer(int $orderId, float $amount): void
{
// Span will be reported as "charge-customer".
}
}