﻿---
title: Attribute-based instrumentation
description: Create spans automatically using PHP 8 attributes with the Elastic Distribution of OpenTelemetry PHP (EDOT PHP).
url: https://www.elastic.co/elastic/docs-builder/docs/3557/reference/opentelemetry/edot-sdks/php/attribute-instrumentation
products:
  - Elastic Cloud Serverless
  - Elastic Distribution of OpenTelemetry PHP
  - Elastic Distribution of OpenTelemetry SDK
  - Elastic Observability
applies_to:
  - Serverless Observability projects: Generally available
  - Elastic Stack: Generally available
  - Elastic Distribution of OpenTelemetry PHP: Planned
---

# 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.

## Prerequisites

- PHP 8.1 or later (PHP attributes require PHP 8.1+).
- `open-telemetry/api` package installed in your application.
- `OTEL_PHP_ATTR_HOOKS_ENABLED=true` set in the environment (deactivated by default).


## Activate

Attribute-based instrumentation is disabled by default. Enable it using either an environment variable or the `php.ini` file:
<tab-set>
  <tab-item title="Environment variable (preferred)">
    ```bash
    export OTEL_PHP_ATTR_HOOKS_ENABLED=true
    ```
  </tab-item>

  <tab-item title="Environment variable (deprecated)">
    ```bash
    export ELASTIC_OTEL_ATTR_HOOKS_ENABLED=true
    ```
  </tab-item>

  <tab-item title="php.ini (preferred)">
    ```ini
    opentelemetry_distro.attr_hooks_enabled=true
    ```
  </tab-item>

  <tab-item title="php.ini (deprecated)">
    ```ini
    elastic_otel.attr_hooks_enabled=true
    ```
  </tab-item>
</tab-set>


## Basic usage

```php
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]` options

```php
#[WithSpan(
    span_name: 'custom.span.name',          // default: "ClassName::methodName"
    span_kind: SpanKind::KIND_SERVER,       
    attributes: ['key' => 'value'],         
)]
```

All arguments are optional. You can pass them positionally or by name.
```php
// 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)]
```


## Capturing parameter values with `#[SpanAttribute]`

Add `#[SpanAttribute]` to function parameters to include their runtime values as span attributes:
```php
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 {
        // ...
    }
}
```


## Capturing property values with `#[SpanAttribute]`

Apply `#[SpanAttribute]` to class properties to capture their value at the time the method is called:
```php
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
    }
}
```


## Exception recording

If the annotated method throws an exception, the span automatically records it and sets status to `ERROR`. The exception propagates normally.
```php
#[WithSpan]
public function riskyOperation(): void
{
    throw new \RuntimeException('something went wrong');
    // Span is ended with STATUS_ERROR and exception event attached.
}
```


## Nested spans

Calling one `#[WithSpan]` method from another creates nested spans automatically:
```php
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 {}
}
```


## Standalone functions

`#[WithSpan]` works on standalone functions, not only methods:
```php
#[WithSpan('compute.result')]
function computeResult(#[SpanAttribute] int $input): int
{
    return $input * 2;
}
```


## Standard span attributes

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              |


## Compatibility

`#[WithSpan]` and `#[SpanAttribute]` are the same PHP attributes used by the official [opentelemetry-php-instrumentation](https://github.com/open-telemetry/opentelemetry-php-instrumentation) extension. Applications already using that extension can activate this feature without code changes.