danilovl/open-telemetry-bundle
Installation:
composer require danilovl/open-telemetry-bundle
Ensure OTEL_PHP_AUTOLOAD_ENABLED=false in your .env.
Configure Environment Variables:
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_SERVICE_NAME=my-app
Basic Configuration:
Create config/packages/open_telemetry.yaml:
danilovl_open_telemetry:
service:
name: 'my-app'
instrumentation:
http_server:
enabled: true
tracing:
enabled: true
First Use Case: Trigger an HTTP request. The bundle automatically instruments the request with OpenTelemetry tracing.
config/packages/open_telemetry.yaml to enable/disable instrumentations.Selective Instrumentation:
Enable only the instrumentations you need (e.g., http_server, doctrine, messenger) to avoid overhead:
danilovl_open_telemetry:
instrumentation:
http_server:
enabled: true
doctrine:
enabled: true
redis:
enabled: false # Disable if unused
Custom Metrics/Traces: Implement interfaces to override default behavior:
// src/Tracing/CustomDoctrineMetrics.php
namespace App\Tracing;
use Danilovl\OpenTelemetryBundle\Instrumentation\Doctrine\DoctrineMetricsInterface;
class CustomDoctrineMetrics implements DoctrineMetricsInterface
{
public function recordQueryExecution(string $query, float $duration): void
{
// Custom logic
}
}
Register the service in services.yaml:
services:
App\Tracing\CustomDoctrineMetrics:
tags: ['danilovl.open_telemetry.metrics.doctrine']
Traceable Attributes:
Annotate controllers/commands/methods with #[Traceable] for automatic span creation:
use Danilovl\OpenTelemetryBundle\Attribute\Traceable;
#[Traceable]
public function processOrder(Order $order): void
{
// Automatically wrapped in a span
}
Middleware/Decorators: Leverage built-in decorators for HTTP clients, caches, and Redis:
# Automatically decorates http_client.client services
danilovl_open_telemetry:
instrumentation:
http_client:
enabled: true
Debugging Traces:
Use the TracingSpanService to manually create spans:
use Danilovl\OpenTelemetryBundle\Service\TracingSpanService;
public function __construct(private TracingSpanService $tracingSpanService) {}
public function someMethod()
{
$span = $this->tracingSpanService->startSpan('custom-span');
try {
// Business logic
} finally {
$span->end();
}
}
Metrics Collection: Inject metrics interfaces into services:
use Danilovl\OpenTelemetryBundle\Instrumentation\HttpServer\HttpServerMetricsInterface;
public function __construct(private HttpServerMetricsInterface $metrics) {}
public function onKernelRequest(GetResponseEvent $event): void
{
$this->metrics->recordRequestStart();
}
Custom Exporters/Processors: Tag services to override default SDK behavior:
services:
App\Tracing\CustomSpanProcessor:
tags:
- { name: 'otel.span_processor', priority: 1000 }
Symfony Messenger:
Enable long_running_command_enabled for messenger:consume:
danilovl_open_telemetry:
instrumentation:
messenger:
enabled: true
long_running_command_enabled: true
Doctrine:
Use default_trace_ignore_enabled to skip vendor queries:
danilovl_open_telemetry:
instrumentation:
doctrine:
enabled: true
default_trace_ignore_enabled: true
HTTP Client: Decorate specific clients by tagging them:
services:
http_client.my_client:
decorates: http_client
arguments:
$decorated: '@http_client.my_client.inner'
tags: ['http_client.client']
Async Operations:
Use #[Traceable] on async methods or enable async instrumentation:
danilovl_open_telemetry:
instrumentation:
async:
enabled: true
PHP Extension Conflict:
Error: OTEL_PHP_AUTOLOAD_ENABLED must be false; otherwise, the bundle and extension will conflict.
Fix: Set OTEL_PHP_AUTOLOAD_ENABLED=false in .env.
Missing Dependencies:
Error: Class 'OpenTelemetry\API\Trace\TracerProviderInterface' not found.
Fix: Ensure open-telemetry/api and open-telemetry/sdk are installed.
Duplicate Implementations:
Error: LogicException: Multiple implementations found for interface X. Declare an alias explicitly.
Fix: Configure the alias in services.yaml:
services:
_defaults:
bind:
Danilovl\OpenTelemetryBundle\Instrumentation\Doctrine\DoctrineMetricsInterface: '@App\Tracing\CustomDoctrineMetrics'
Late Initialization:
Issue: Traces/metrics appear only after the first request.
Fix: The SDK initializes lazily. For immediate startup, trigger a dummy request or use OpenTelemetryInitializer::initializeSdk() manually.
Context Propagation:
Issue: Spans not linked across services (e.g., HTTP → Messenger).
Fix: Ensure OTEL_TRACES_SAMPLER is configured (e.g., always_on for testing):
OTEL_TRACES_SAMPLER=always_on
Check Initialization: Enable debug mode and inspect logs for:
[OpenTelemetry] Initializing SDK with resource attributes: {...}
Validate Configuration: Use Symfony’s config validator:
php bin/console debug:config danilovl_open_telemetry
Test Locally: Run a local OTel Collector with Docker:
docker run -p 4318:4318 otel/opentelemetry-collector
Then configure:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
Span Visualization: Use Jaeger or Kibana to verify traces:
docker run -p 16686:16686 jaegertracing/all-in-one:latest
Configure exporter:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
Performance Tuning:
Disable unused instrumentations (e.g., twig, events) to reduce overhead.
Custom Attributes: Extend spans with context via providers:
use Danilovl\OpenTelemetryBundle\Provider\SpanAttributeProviderInterface;
class UserIdSpanAttributeProvider implements SpanAttributeProviderInterface
{
public function getAttributes(): array
{
return ['user_id' => $this->userIdService->getId()];
}
}
Register as a service tagged danilovl.open_telemetry.span_attribute_provider.
Sampling:
Configure sampling in .env:
OTEL_TRACES_SAMPLER=parentbased_always_on
OTEL_TRACES_SAMPLER_ARG=1.0 # Sample 100% of traces
Environment-Specific Configs: Use Symfony’s environment-aware configs:
# config/packages/dev/open_telemetry.yaml
danilovl_open_telemetry:
service:
environment: 'dev'
instrumentation:
http_server:
tracing:
enabled: true
Testing: Mock the `
How can I help you explore Laravel packages today?