Attribute-based instrumentation
EDOT PHP supports automatic span creation using PHP 8.1 attributes. Annotate methods or functions with #[WithSpan] to create spans without writing instrumentation code manually.
- PHP 8.1 or later (PHP attributes require PHP 8.1+).
open-telemetry/apipackage installed in your application.OTEL_PHP_ATTR_HOOKS_ENABLED=trueset in the environment (deactivated by default).
Attribute-based instrumentation is disabled by default. Enable it using either an environment variable or the php.ini file:
export OTEL_PHP_ATTR_HOOKS_ENABLED=true
export ELASTIC_OTEL_ATTR_HOOKS_ENABLED=true
opentelemetry_distro.attr_hooks_enabled=true
elastic_otel.attr_hooks_enabled=true
use OpenTelemetry\API\Instrumentation\WithSpan;
class OrderService
{
#[WithSpan]
public function processOrder(int $orderId): string
{
// A span named "OrderService::processOrder" is created automatically.
return "processed-{$orderId}";
}
}
#[WithSpan(
span_name: 'custom.span.name', // default: "ClassName::methodName"
span_kind: SpanKind::KIND_SERVER,
attributes: ['key' => 'value'],
)]
- default: KIND_INTERNAL
- static attributes added to the span
All arguments are optional. You can pass them positionally or by name.
// Positional
#[WithSpan('payment.charge', SpanKind::KIND_CLIENT, ['db.system' => 'redis'])]
// Named — any subset
#[WithSpan(span_kind: SpanKind::KIND_PRODUCER)]
#[WithSpan(span_name: 'message.publish', span_kind: SpanKind::KIND_PRODUCER)]
Add #[SpanAttribute] to function parameters to include their runtime values as span attributes:
use OpenTelemetry\API\Instrumentation\WithSpan;
use OpenTelemetry\API\Instrumentation\SpanAttribute;
class UserService
{
#[WithSpan]
public function createUser(
#[SpanAttribute] string $username, // attribute key = "username"
string $password,
#[SpanAttribute('user.email')] string $email, // attribute key = "user.email"
): int {
// ...
}
}
- not captured
Apply #[SpanAttribute] to class properties to capture their value at the time the method is called:
class InvoiceService
{
#[SpanAttribute]
public string $customerId = '';
#[SpanAttribute('invoice.currency')]
public string $currency = 'EUR';
#[WithSpan('invoice.generate')]
public function generate(): string
{
// Span attributes include: customerId, invoice.currency
}
}
If the annotated method throws an exception, the span automatically records it and sets status to ERROR. The exception propagates normally.
#[WithSpan]
public function riskyOperation(): void
{
throw new \RuntimeException('something went wrong');
// Span is ended with STATUS_ERROR and exception event attached.
}
Calling one #[WithSpan] method from another creates nested spans automatically:
class Pipeline
{
#[WithSpan('pipeline.run')]
public function run(): void
{
$this->step1(); // child span: "pipeline.step1"
$this->step2(); // child span: "pipeline.step2"
}
#[WithSpan('pipeline.step1')]
private function step1(): void {}
#[WithSpan('pipeline.step2')]
private function step2(): void {}
}
#[WithSpan] works on standalone functions, not only methods:
#[WithSpan('compute.result')]
function computeResult(#[SpanAttribute] int $input): int
{
return $input * 2;
}
Every #[WithSpan] span includes these attributes from the declaration site:
| Attribute | Value |
|---|---|
code.function |
Function or method name |
code.namespace |
Class name (empty for standalone functions) |
code.filepath |
Source file path |
code.lineno |
Line number of the declaration |
#[WithSpan] and #[SpanAttribute] are the same PHP attributes used by the official opentelemetry-php-instrumentation extension. Applications already using that extension can activate this feature without code changes.