open-telemetry/context
OpenTelemetry Context for PHP: immutable, execution-scoped context propagation for tracing and telemetry. Activate/detach scopes for implicit propagation, with debug warnings for scope leaks. Supports async apps with fiber-based propagation and event loop binding.
Install the package:
composer require open-telemetry/context
Activate a context in your Laravel middleware, service, or command:
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\Scope;
$context = Context::getCurrent();
$scope = $context->activate();
try {
// Your code here (e.g., HTTP request, queue job, fiber)
} finally {
$scope->detach(); // Critical: Always detach!
}
First Use Case: Instrument a Laravel HTTP request to propagate trace context:
// In a middleware or controller
$scope = Context::getCurrent()->activate();
try {
$response = Http::get('https://api.example.com/endpoint');
return response()->json($response->json());
} finally {
$scope->detach();
}
Context::getCurrent(): Retrieve the current context (e.g., for middleware, services).Context::withBaggage(): Attach custom metadata (e.g., user_id, tenant_id) to traces.Context::withSpan(): Create or modify spans (requires opentelemetry-php/sdk).bindContext(): Wrap async callbacks (fibers, event loops) for automatic propagation.Pattern: Propagate context via middleware or HTTP clients (Guzzle, Symfony).
// app/Http/Middleware/TraceContext.php
public function handle($request, Closure $next) {
$scope = Context::getCurrent()->activate();
try {
return $next($request);
} finally {
$scope->detach();
}
}
Integration Tip:
OpenTelemetry\Context\Propagation\TextMapPropagator to inject/extract context from headers (e.g., traceparent).$client = new Client([
'on_request' => function (RequestInterface $request) {
$propagator = new TextMapPropagator();
$propagator->inject(Context::getCurrent(), $request->getHeaders());
},
]);
Pattern: Automate context propagation in async code.
Enable with:
OTEL_PHP_FIBERS_ENABLED=true php your_script.php
Example:
// In a fiber or Swoole coroutine
$fiber = new Fiber(function () {
$scope = Context::getCurrent()->activate();
try {
// Fiber logic (context auto-propagated)
} finally {
$scope->detach();
}
});
$fiber->start();
Pattern: Use bindContext() to wrap callbacks.
use OpenTelemetry\Context\bindContext;
// ReactPHP example
$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(1, bindContext(function () {
// Context automatically propagated
}));
Integration Tip:
Swoole\Coroutine::create(bindContext(fn() => {...})).finally blocks.Pattern: Propagate context to async jobs.
// In your job class
public function handle() {
$scope = Context::getCurrent()->activate();
try {
// Job logic
} finally {
$scope->detach();
}
}
Integration Tip:
OpenTelemetry\Context\Propagation\TextMapPropagator to inject context into job payloads (serialized).Supervisor to auto-activate context.Pattern: Attach baggage to CLI commands for traceability.
// In a command
protected function handle() {
$context = Context::getCurrent()->withBaggage([
'command' => 'your:command',
'user_id' => auth()->id(),
]);
$scope = $context->activate();
try {
// Command logic
} finally {
$scope->detach();
}
}
Pattern: Attach business metadata to traces.
$context = Context::getCurrent()->withBaggage([
'tenant_id' => 'acme-corp',
'request_id' => $request->header('X-Request-ID'),
]);
$context->activate();
Use Cases:
Forgetting to Detach Scopes
try-finally:
$scope = $context->activate();
try {
// ...
} finally {
$scope->detach(); // Critical!
}
OTEL_PHP_DEBUG_SCOPES_DISABLED=false (default) to catch leaks in dev.Async Context Loss
bindContext().bindContext() for all async callbacks:
$loop->addTimer(1, bindContext(fn() => {...}));
PHP 8.1+ Required for Fibers
ext-ffi.PHP_VERSION_ID >= 80100 and enable OTEL_PHP_FIBERS_ENABLED.Serialization/Deserialization
TextMapPropagator to inject context into headers/payloads:
$propagator = new TextMapPropagator();
$carrier = [];
$propagator->inject(Context::getCurrent(), $carrier);
// Serialize $carrier with job payload
ZTS (Thread-Safety) Issues
Inspect Current Context
$context = Context::getCurrent();
dump($context->getBaggage()->all()); // View baggage
dump($context->getSpan()); // View active span (if SDK is loaded)
Enable Debug Scopes
OTEL_PHP_DEBUG_SCOPES_DISABLED=false to catch undetached scopes in dev.Check Fiber Context
$fiber = new Fiber(function () {
dump(Context::getCurrent()->getBaggage()->all());
});
$fiber->start();
Validate Propagation
TextMapPropagator to verify context injection/extraction:
$propagator = new TextMapPropagator();
$carrier = [];
$propagator->inject($context, $carrier);
$extracted = $propagator->extract([]); // Should match $context
Custom Propagators
PropagatorInterface for non-TextMap formats (e.g., gRPC metadata).Context Storage
ContextStorage for custom storage backends (e.g., Redis for distributed contexts).Baggage Validation
Baggage to add custom validation rules:
$baggage = new Baggage([
'user_id' => '123',
]);
$baggage->validate(fn($key, $value) => is_numeric($value)); // Custom rule
Async Hooks
bindContext() for custom async systems (e.g., custom event loops).Avoid Heavy Baggage
Scope Granularity
Fiber Overhead
Disable Debug in Production
OTEL_PHP_DEBUG_SCOPES_DISABLED=true to avoid warnings in prod.How can I help you explore Laravel packages today?