Skip to content

Distributed Tracing and Telemetry with OpenTelemetry

Winter Boot provides first-class integration with OpenTelemetry (OTel), covering all three observability pillars: distributed traces, metrics, and structured logs. The integration is delivered as an optional module (OpenTelemetryModule) so it has zero overhead when disabled. Once enabled, you can instrument web requests automatically via an interceptor, annotate individual methods for fine-grained tracing or counting, and inject telemetry beans anywhere in your application.

Complete all four setup steps before enabling the module.

  1. Install the PHP OpenTelemetry extension

    Terminal window
    pecl install opentelemetry

    Then enable it in your php.ini:

    php.ini
    extension=opentelemetry.so
  2. Install the Composer packages

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

    application.yml
    modules:
    - module: 'dev\winterframework\telemetry\OpenTelemetryModule'
    enabled: true
    configFile: /path/to/opentelemetry.yaml
  4. Create opentelemetry.yaml

    opentelemetry.yaml
    winter:
    telemetry:
    serviceName: 'my-winter-application'
    exporter:
    type: 'otlp'
    endpoint: 'http://localhost:4317'
    sampler:
    type: 'parent_based_always_on'
    ratio: 1.0

    Key configuration fields:

    • serviceName string (required) Identifies this service in your observability backend (Jaeger, Tempo, etc.).

    • exporter.type string (required) Export protocol — otlp (gRPC/HTTP), zipkin, jaeger, or console.

    • exporter.endpoint string (required) The OTel collector or backend endpoint URL.

    • sampler.type string (default: parent_based_always_on) Sampling strategy — always_on, always_off, parent_based_always_on, or traceidratio.

    • sampler.ratio float (default: 1.0) Sampling ratio (0.0–1.0) used when the traceidratio sampler is selected.

Register OpenTelemetryWebInterceptor in your WebMvcConfigurer to automatically 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\web\config\WebMvcConfigurer;
use dev\winterframework\web\config\InterceptorRegistry;
#[Configuration]
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".
}
}