eerzho/opentelemetry-auto-class
Framework-agnostic automatic OpenTelemetry tracing for PHP 8.2+ using #[Traceable]. Mark a class and public methods generate spans via ext-opentelemetry hook API. Supports excluding methods; Laravel/Symfony integrations available.
Install the Package:
composer require eerzho/opentelemetry-auto-class
Ensure ext-opentelemetry is installed and enabled in your PHP environment.
Annotate a Class:
Add the #[Traceable] attribute to any class to automatically trace all public methods:
use OpenTelemetry\Contrib\Instrumentation\Class\Attribute\Traceable;
#[Traceable]
class UserService
{
public function createUser(array $data): void
{
// Automatically traced
}
}
Register Instrumentation:
Scan and register the class for tracing in your application’s bootstrap (e.g., AppServiceProvider):
use OpenTelemetry\Contrib\Instrumentation\Class\AttributeScanner;
use OpenTelemetry\Contrib\Instrumentation\Class\ClassInstrumentation;
public function boot()
{
$map = AttributeScanner::scan([UserService::class]);
ClassInstrumentation::register($map);
}
Verify Tracing:
Trigger the traced method and check your OpenTelemetry backend (e.g., Jaeger, Honeycomb) for the generated span named UserService::createUser.
For a Laravel application, trace controller methods to monitor API performance:
#[Traceable]
class OrderController extends Controller
{
public function store(Request $request): JsonResponse
{
// Automatically traced as "OrderController::store"
$order = Order::create($request->all());
return response()->json($order);
}
}
Register the controller in AppServiceProvider:
ClassInstrumentation::register(
AttributeScanner::scan([OrderController::class])
);
#[Traceable] to enable tracing for all public methods.#[Traceable(exclude: ['healthCheck'])]
class PaymentGateway
{
public function processPayment(float $amount): void
{
// Traced
}
public function healthCheck(): bool
{
// Excluded from tracing
return true;
}
}
ClassInstrumentation::register([
UserRepository::class => [
'findById' => ['id' => 0], // Capture 'id' argument
'save' => [], // Trace without arguments
],
]);
#[Arguments(exclude: [...])] to hide sensitive data (e.g., passwords, tokens).#[Traceable]
class AuthService
{
#[Arguments(exclude: ['password'])]
public function login(string $email, string $password): void
{
// Only 'email' is captured as a span attribute
}
}
eerzho/opentelemetry-auto-class-laravel) for automatic class discovery.composer require eerzho/opentelemetry-auto-class-laravel
config/opentelemetry.php:
'instrumentation' => [
'class' => [
'enabled' => true,
'scanned_classes' => [
App\Services\*,
App\Http\Controllers\*,
],
],
],
Middleware Integration: Trace HTTP requests by annotating middleware:
#[Traceable]
class LogRequestMiddleware
{
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
}
Register it in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\LogRequestMiddleware::class,
];
Queue Jobs: Annotate dispatched jobs to trace async workflows:
#[Traceable]
class ProcessOrderJob implements ShouldQueue
{
public function handle(): void
{
// Traced as "ProcessOrderJob::handle"
}
}
Propagate Context: Ensure context is propagated across HTTP requests or queue workers by configuring your OTel SDK:
use OpenTelemetry\API\Propagation\TextMapPropagator;
$propagator = new TextMapPropagator();
$propagator->inject($carrier, $context);
In Laravel, use middleware to inject context into HTTP headers:
public function handle($request, Closure $next)
{
$carrier = [];
$propagator->extract($carrier, $request->header());
$context = $propagator->extract($carrier, $request->header());
// Attach context to the request or span
return $next($request);
}
Custom Span Attributes: Extend spans with custom attributes by accessing the active span in your methods:
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\TracerInterface;
#[Traceable]
class OrderService
{
public function __construct(private TracerInterface $tracer) {}
public function create(array $items): void
{
$span = $this->tracer->getCurrentSpan();
$span->setAttribute('order.type', 'premium');
}
}
#[Traceable]
class UserRepository
{
public function getUser(int $id): User
{
return $this->fetchUser($id); // Private method NOT traced
}
private function fetchUser(int $id): User
{
return User::find($id);
}
}
$this->app->singleton('otel.class.map', function () {
return AttributeScanner::scan([UserService::class, OrderService::class]);
});
- Use the Laravel integration to avoid manual scanning.
"resource").#[Arguments(exclude: ['callback'])]
public function execute(callable $callback): void {}
traceparent headers are set in HTTP requests.OTEL_TRACES_SAMPLER to force sampling for debugging:
OTEL_TRACES_SAMPLER=AlwaysOn
if (!ClassInstrumentation::isRegistered(UserService::class)) {
ClassInstrumentation::register($map);
}
OTEL_PHP_DISABLED_INSTRUMENTATIONS=class
Or programmatically:
putenv('OTEL_PHP_DISABLED_INSTRUMENTATIONS=class');
docker run -d -p 16686:16686 jaegertracing/all-in-one:1.35
Then query for spans with names like UserService::createUser.How can I help you explore Laravel packages today?