open-telemetry/sdk
OpenTelemetry PHP SDK for generating traces, metrics, and logs. Implements the API and works with exporters to emit telemetry. Supports manual setup, an SDK builder, and optional auto-registration via environment variables during Composer autoload.
## Getting Started
### Minimal Setup
1. **Install the package**:
```bash
composer require open-telemetry/sdk
Enable autoloading (recommended for most Laravel apps):
Set these environment variables in .env:
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME="your-laravel-app"
OTEL_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4318"
First use case - Tracing a request:
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\TracerInterface;
$tracer: TracerInterface = Globals::tracerProvider()->getTracer(__CLASS__);
$span = $tracer->spanBuilder('process-request')->startSpan();
try {
// Your request logic here
$span->addEvent('request-processed');
} finally {
$span->end();
}
First use case - Metrics:
$meter = Globals::meterProvider()->getMeter(__CLASS__);
$counter = $meter->createCounter('http_requests');
$counter->add(1, ['method' => 'GET', 'path' => '/api/users']);
// In Laravel HTTP middleware
public function handle($request, Closure $next)
{
$tracer = Globals::tracerProvider()->getTracer(__CLASS__);
$span = $tracer->spanBuilder('http-request')
->setAttribute('http.method', $request->method())
->setAttribute('http.url', $request->fullUrl())
->startSpan();
try {
$response = $next($request);
$span->setAttribute('http.status_code', $response->status());
return $response;
} finally {
$span->end();
}
}
// In a service using Laravel's DB
public function getUser($id)
{
$tracer = Globals::tracerProvider()->getTracer(__CLASS__);
$span = $tracer->spanBuilder('db-query')->startSpan();
try {
$span->setAttribute('db.statement', 'SELECT * FROM users WHERE id = ?');
$user = DB::table('users')->where('id', $id)->first();
$span->setAttribute('db.result_count', 1);
return $user;
} finally {
$span->end();
}
}
// In a controller or service
public function processOrder()
{
$meter = Globals::meterProvider()->getMeter(__CLASS__);
$orderCounter = $meter->createCounter('orders.processed');
$orderCounter->add(1, ['status' => 'success']);
$histogram = $meter->createHistogram('order.processing.time');
$histogram->record(125.3, ['order_id' => 12345]);
}
// Incoming request with context
public function handleIncomingRequest($request)
{
$context = \OpenTelemetry\Context\Context::current();
$propagator = \OpenTelemetry\Context\Propagator::getTextMapPropagator();
// Inject context into headers
$propagator->inject($context, $request->headers->all());
// Later, when processing the request
$newContext = $propagator->extract(
\OpenTelemetry\Context\Context::getCurrent(),
$request->headers->all()
);
}
Laravel Service Provider Integration:
public function register()
{
$this->app->singleton(TracerInterface::class, function () {
return Globals::tracerProvider()->getTracer('laravel-app');
});
}
Queue Job Instrumentation:
public function handle()
{
$tracer = app(TracerInterface::class);
$span = $tracer->spanBuilder('process-job')->startSpan();
try {
// Job logic
} finally {
$span->end();
}
}
Custom Span Processors:
use OpenTelemetry\SDK\Trace\SpanProcessor\SpanProcessorInterface;
class CustomSpanProcessor implements SpanProcessorInterface
{
public function onStart(\OpenTelemetry\API\Trace\Span $span, \OpenTelemetry\Context\Context $parentContext)
{
// Custom logic on span start
}
public function onEnd(\OpenTelemetry\API\Trace\Span $span)
{
// Custom logic on span end
}
}
// Register in SDK builder
$spanProcessor = new CustomSpanProcessor();
$sdk->addSpanProcessor($spanProcessor);
Resource Configuration:
use OpenTelemetry\SDK\Resource\ResourceInfo;
$resource = new ResourceInfo([
'service.name' => 'laravel-app',
'service.version' => '1.0.0',
'deployment.environment' => env('APP_ENV', 'development'),
]);
$sdk->setResource($resource);
Autoloading Issues:
OTEL_PHP_AUTOLOAD_ENABLED=true but configuration is missing, you'll get no-op implementationsSpan Leaks:
try-finally blocks or Laravel's ensureClosed() pattern$span->ensureClosed(); // Laravel 9+ helper
Context Management:
Context::with() and Context::current() explicitly when neededSampling Configuration:
$sampler = new \OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler();
$sdk->setSampler($sampler);
Attribute Limits:
setAttribute() judiciouslyEnable Debug Logging:
OTEL_PHP_LOG_LEVEL=debug
OTEL_PHP_LOG_FORMAT=json
Check Exporter Status:
$exporter = $sdk->getSpanExporter();
if (!$exporter->isExporting()) {
// Handle export failure
}
Validate Configuration:
use OpenTelemetry\SDK\Common\Configuration;
$config = Configuration::get();
if ($config->getExporter() === null) {
throw new \RuntimeException('No exporter configured');
}
Common Errors and Fixes:
| Error | Solution |
|---|---|
No active tracer provider |
Ensure SDK is properly initialized |
Exporter not found |
Verify OTEL_EXPORTER environment variable |
Invalid span name |
Span names must be <= 256 chars, no control chars |
Context propagation failed |
Check header names match propagator expectations |
Sampling Strategies:
TraceIdRatioBasedSampler for production:$sampler = new \OpenTelemetry\SDK\Trace\Sampler\TraceIdRatioBasedSampler(0.1); // 10% sampling
Batch Exporting:
$spanProcessor = new \OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessor(
new \OpenTelemetry\SDK\Trace\Export\InMemorySpanExporter(),
5000, // Schedule delay (ms)
100 // Max queue size
);
Attribute Optimization:
AttributesBuilder for efficient attribute setting:$span->setAttributes(
\OpenTelemetry\API\Common\AttributesBuilder::create()
->putStringAttribute('user.id', $userId)
->put
How can I help you explore Laravel packages today?