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.
opentelemetry PHP extensionUnlike 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)
peclavailable — 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
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
| Package | What it does |
|---|---|
open-telemetry/sdk | The core SDK — tracer providers, span processors, resource detection |
open-telemetry/exporter-otlp | Ships 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"
| Variable | Purpose |
|---|---|
OTEL_PHP_AUTOLOAD_ENABLED | Required — turns on the auto-instrumentation runner. Without this, the extension is loaded but doesn't trace anything. |
OTEL_SERVICE_NAME | The name that appears in CodeXray's Applications list |
OTEL_EXPORTER_OTLP_ENDPOINT | Your CodeXray collector URL (no trailing slash) |
OTEL_EXPORTER_OTLP_PROTOCOL | http/protobuf (recommended) or grpc |
OTEL_EXPORTER_OTLP_HEADERS | Authentication — your project API key as x-api-key |
OTEL_RESOURCE_ATTRIBUTES | Optional — 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
- Make a request to an instrumented route (any endpoint your framework handles)
- In CodeXray, open Applications → find your service → Traces tab
- 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
- Traces Overview — the full Traces tab walkthrough
- OpenTelemetry for Node.js · for Next.js — instrument your JavaScript services
- eBPF-based Tracing — immediate coverage for services you can't modify