open-telemetry/sdk
OpenTelemetry PHP SDK implementation. Configure manually, via SDK Builder, or enable autoloading with OTEL_PHP_AUTOLOAD_ENABLED and environment-based settings. Use with exporters to generate and export traces, metrics, and other telemetry.
composer require open-telemetry/sdk
.env:
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=your-laravel-app
OTEL_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4318
use OpenTelemetry\API\Globals;
Route::get('/example', function () {
$tracer = Globals::tracerProvider()->getTracer(__CLASS__);
$span = $tracer->spanBuilder('route.example')->startSpan();
$span->activate();
// Your logic here
$result = doSomething();
$span->end();
return $result;
});
Globals::tracerProvider()->getTracer('your-service')Globals::meterProvider()->getMeter('your-service')Globals::loggerProvider()->getLogger('your-service')Add to bootstrap/app.php (after createApplication()):
$app->register(\OpenTelemetry\Contrib\Laravel\OpenTelemetryServiceProvider::class);
// In AppServiceProvider boot()
$this->app->register(\OpenTelemetry\Contrib\Laravel\Http\OpenTelemetryMiddleware::class);
opentelemetry/ext-db for PDO/Query Builder instrumentation.// Service layer
public function processOrder(Order $order) {
$tracer = Globals::tracerProvider()->getTracer(__CLASS__);
$span = $tracer->spanBuilder('order.process')
->setAttribute('order.id', $order->id)
->startSpan();
try {
$span->activate();
// Business logic
$span->setStatus(new Status(StatusCode::OK, 'Order processed'));
} catch (\Exception $e) {
$span->recordException($e);
$span->setStatus(new Status(StatusCode::ERROR, $e->getMessage()));
throw $e;
} finally {
$span->end();
}
}
// Track API response times
$meter = Globals::meterProvider()->getMeter(__CLASS__);
$histogram = $meter->createHistogram('api.response.time', [
'description' => 'API response time in milliseconds',
'unit' => 'ms'
]);
// In your controller
$start = microtime(true);
$response = $client->request('GET', '/api/endpoint');
$histogram->record(microtime(true) - $start, [
'endpoint' => 'api/endpoint',
'status' => $response->getStatusCode()
]);
// Structured logging with context
$logger = Globals::loggerProvider()->getLogger(__CLASS__);
$logger->info('Order processed', [
'order_id' => $order->id,
'user_id' => auth()->id(),
'span_context' => Globals::span()->getSpanContext()
]);
# .env
OTEL_TRACES_SAMPLER=always_on # For development
OTEL_TRACES_EXPORTER=console # For local debugging
OTEL_METRICS_EXPORTER=otlp
OTEL_SERVICE_VERSION=1.0.0
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
$tracerProvider = new TracerProvider(
new SpanProcessor\BatchSpanProcessor(
new SimpleSpanProcessor(),
new AlwaysOnSampler()
)
);
Globals::setTracerProvider($tracerProvider);
// app/Http/Middleware/TraceContext.php
public function handle($request, Closure $next) {
$propagator = new \OpenTelemetry\Context\Propagator\TextMapPropagator();
$context = $propagator->extract(
\OpenTelemetry\Context\Context::getCurrent(),
$request->header()
);
return $next($request)->withHeaders(
$propagator->inject($context, [])
);
}
// app/Providers/OpenTelemetryServiceProvider.php
public function register() {
$this->app->singleton(TracerProvider::class, function () {
return new TracerProvider(
new BatchSpanProcessor(
new OtlpSpanExporter([/* config */]),
new AlwaysOnSampler()
)
);
});
}
Autoloading Failures
Globals::tracerProvider() returns a no-op if autoloading fails.bootstrap/app.php:
if (!Globals::tracerProvider()->getTracer('test')) {
$provider = new TracerProvider();
Globals::setTracerProvider($provider);
}
Sampling Misconfiguration
TraceIdRatioBasedSampler with 0.0 ratio silently drops all traces.AlwaysOnSampler in development:
$sampler = new AlwaysOnSampler();
Resource Attributes Override
$resource = new Resource([
'service.name' => 'your-app',
'deployment.environment' => env('APP_ENV')
]);
$provider = new TracerProvider([], $resource);
Span Context Leaks
public function terminate($request, $response) {
Globals::span()->detach();
}
Metric Naming Collisions
$meter->getMeter('app.metrics.api');
Console Exporter for Local Testing
OTEL_TRACES_EXPORTER=console
OTEL_METRICS_EXPORTER=console
OTEL_LOGS_EXPORTER=console
Span Visualization
span->setAttribute('http.url', $url) for better Jaeger/ZPages views.span->setAttribute('db.statement', $query) for database spans.Performance Overhead
MeterProvider with batching:
$meterProvider = new MeterProvider([
new PeriodicExportingMetricReader(
new OtlpMetricExporter([/* config */]),
5000 // Export every 5 seconds
)
]);
Custom Span Processors
class FilteringSpanProcessor implements SpanProcessorInterface {
public function onStart(Span $span, ?Span $parentSpan = null) {
if ($span->getName() === 'internal.healthcheck') {
$span->setAttribute('internal', true);
}
}
public function onEnd(Span $span) {}
}
Dynamic Configuration
// Load config from cache
$config = cache()->remember('otel_config', 60, function() {
return \OpenTelemetry\SDK\Configuration::loadFromEnv();
});
Laravel Job Instrumentation
// In Handle trait
public function handle() {
$span = Globals::tracerProvider()->getTracer(__CLASS__)
->spanBuilder('job.'.class_basename($this))
->startSpan();
try {
$span->activate();
// Job logic
} finally {
$span->end();
}
}
OTEL_CONFIG_FILE) overrides environment variables.otel-config.yaml:
exporters:
otlp:
endpoint
How can I help you explore Laravel packages today?