Attribute-based instrumentation
Elastic Cloud Serverless projects are autoscaled environments, fully managed by Elastic and available on Elastic Cloud. Availability This functionality is generally available and ready for production usage. The Elastic Stack includes Elastic's core products such as Elasticsearch, Kibana, Logstash, and Beats. Availability This functionality is generally available and ready for production usage. Unless stated otherwise on the page, this functionality is available when your Elastic Stack is deployed on Elastic Cloud Hosted, Elastic Cloud Enterprise, Elastic Cloud on Kubernetes, and self-managed environments. This documentation corresponds to the latest patch available for each minor version. If you're not using the latest patch, check the release notes for changes. The Elastic Distribution of OpenTelemetry (EDOT) PHP SDK collects performance metrics, traces, and logs in OpenTelemetry format, and sends them to Elastic Observability. Availability This functionality is generally available and ready for production usage. This documentation corresponds to the latest patch available for each minor version. If you're not using the latest patch, check the release notes for changes.
Serverless Observability
Generally available
Stack
Generally available
EDOT PHP
1.7+
Generally available since 1.7
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 deactivated 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.