Skip to main content

OpenTelemetry for PHP

OpenTelemetry for PHP supports zero-code instrumentation via a native PHP extension that hooks into the runtime — once installed, popular frameworks (Laravel, Symfony, Slim, any PSR-15 app) emit HTTP, database, and cache spans automatically. Traces ship to CodeXray over OTLP.

Zero-code via the opentelemetry PHP extension

Unlike Go or Next.js, PHP gets first-class auto-instrumentation: the opentelemetry PECL extension intercepts framework code at runtime, so adding tracing usually means installing two things and setting four env vars — no source changes.


Before You Begin​

You'll need:

  • A PHP application (PHP 8.0+) running with OPcache enabled (required by the extension)
  • The URL of your CodeXray collector (e.g., https://your-codexray.example.com)
  • A project API key from the CodeXray UI (Project Settings → API Keys)
  • pecl available — comes bundled with most PHP installations

Step 1 — Install the OpenTelemetry PHP Extension​

The extension is what makes zero-code instrumentation possible. Install it via PECL:

pecl install opentelemetry

Then enable it in your php.ini by adding:

extension=opentelemetry

Verify it loaded:

php -m | grep opentelemetry
# Expected output: opentelemetry
OPcache is required

The extension uses OPcache hooks to instrument framework code. Make sure opcache.enable=1 (and opcache.enable_cli=1 if you trace CLI scripts) is set in php.ini.


Step 2 — Install the OpenTelemetry SDK and Exporter​

composer require \
open-telemetry/sdk \
open-telemetry/exporter-otlp
PackageWhat it does
open-telemetry/sdkThe core SDK — tracer providers, span processors, resource detection
open-telemetry/exporter-otlpShips spans to your CodeXray collector over OTLP HTTP

Step 3 — Install Framework-Specific Auto-Instrumentation​

Pick the package that matches your framework. Each one wires in HTTP, database, and cache spans automatically — no code changes:

# Laravel
composer require open-telemetry/opentelemetry-auto-laravel

# Symfony
composer require open-telemetry/opentelemetry-auto-symfony

# Slim
composer require open-telemetry/opentelemetry-auto-slim

# Any PSR-15 middleware-based app
composer require open-telemetry/opentelemetry-auto-psr15

Database drivers (PDO, MySQLi), HTTP clients (Guzzle), and cache backends are usually picked up by the framework package directly; if you use one standalone, add its dedicated package:

composer require \
open-telemetry/opentelemetry-auto-pdo \
open-telemetry/opentelemetry-auto-guzzle

Step 4 — Configure via Environment Variables​

export OTEL_PHP_AUTOLOAD_ENABLED=true
export OTEL_SERVICE_NAME="your-service-name"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://your-codexray.example.com"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-project-api-key"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
VariablePurpose
OTEL_PHP_AUTOLOAD_ENABLEDRequired — turns on the auto-instrumentation runner. Without this, the extension is loaded but doesn't trace anything.
OTEL_SERVICE_NAMEThe name that appears in CodeXray's Applications list
OTEL_EXPORTER_OTLP_ENDPOINTYour CodeXray collector URL (no trailing slash)
OTEL_EXPORTER_OTLP_PROTOCOLhttp/protobuf (recommended) or grpc
OTEL_EXPORTER_OTLP_HEADERSAuthentication — your project API key as x-api-key
OTEL_RESOURCE_ATTRIBUTESOptional — deployment.environment=production,service.version=1.3.0

For PHP-FPM, set these in your pool config (/etc/php-fpm.d/*.conf):

env[OTEL_PHP_AUTOLOAD_ENABLED] = true
env[OTEL_SERVICE_NAME] = your-service-name
env[OTEL_EXPORTER_OTLP_ENDPOINT] = https://your-codexray.example.com
env[OTEL_EXPORTER_OTLP_PROTOCOL] = http/protobuf
env[OTEL_EXPORTER_OTLP_HEADERS] = x-api-key=your-project-api-key

Reload PHP-FPM (systemctl reload php-fpm) and the next request starts producing spans.


Step 5 — Run Your Application​

No code changes, no command-line flags. Run your service exactly as before:

# Laravel / Symfony built-in server
php artisan serve
php -S 0.0.0.0:8000 -t public/

# php-fpm
systemctl restart php-fpm

# A queue worker / CLI script
php artisan queue:work

The extension activates on startup, the framework package wires in middleware, and the OTLP exporter ships spans in the background.


Verifying It's Working​

  1. Make a request to an instrumented route (any endpoint your framework handles)
  2. In CodeXray, open Applications → find your service → Traces tab
  3. Traces should appear within a minute, named by HTTP method + route (e.g. GET /api/users/{id})

If nothing arrives, set OTEL_LOG_LEVEL=debug and check your PHP error log for OTel exporter messages.


Custom Spans for Business Logic​

Auto-instrumentation handles the HTTP/database/cache spans for free. For your own business logic, add spans manually:

use OpenTelemetry\API\Globals;

$tracer = Globals::tracerProvider()->getTracer('my-service');

function processOrder(string $orderId): void {
global $tracer;
$span = $tracer->spanBuilder('processOrder')->startSpan();
$span->setAttribute('order.id', $orderId);

try {
// your business logic ...
} finally {
$span->end();
}
}

The span automatically becomes a child of whatever HTTP request span is active, so it shows up nested inside the request in the trace viewer.


Next Steps​