Skip to content

PHP

PHP OpenTelemetry SDK

This guide covers how to instrument PHP applications with OpenTelemetry to send traces (and optionally metrics and logs) to Sematext. Both auto-instrumentation (via the OpenTelemetry PHP extension) and manual instrumentation are supported.

Prerequisites

  • PHP 8.2 or newer
  • Composer for dependency management
  • The OpenTelemetry PHP extension (ext-opentelemetry) for auto-instrumentation
  • A Sematext App (create one) and your token
  • One of:

Install the OpenTelemetry PHP extension

Auto-instrumentation requires a PHP extension that hooks into the runtime. Install it with PECL:

pecl install opentelemetry

Enable it in php.ini:

extension=opentelemetry.so

Verify:

php -m | grep opentelemetry

You can skip this extension if you only plan to use manual instrumentation — but auto-instrumentation is much faster to get started with.

Install OpenTelemetry packages

In your project root:

composer require \
  open-telemetry/sdk \
  open-telemetry/exporter-otlp \
  open-telemetry/opentelemetry-auto-laravel

Swap opentelemetry-auto-laravel for the appropriate auto-instrumentation package for your framework (Symfony, Slim, etc.) — see the OpenTelemetry PHP registry for the full list.

Configure environment variables

The OpenTelemetry PHP SDK reads its configuration from environment variables. Pick the flow that matches your setup.

Flow A — Sematext Agent (default in the per-language examples)

export OTEL_PHP_AUTOLOAD_ENABLED=true
export OTEL_SERVICE_NAME=your-service-name
export OTEL_SERVICE_VERSION=1.0.0

export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=none   # auto-instrumentation doesn't ship logs

export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4338
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

OTEL_PHP_AUTOLOAD_ENABLED=true is required for the PHP SDK to bootstrap automatically when the extension is loaded.

Flow B — Managed OTLP endpoint (no agent)

export OTEL_PHP_AUTOLOAD_ENABLED=true
export OTEL_SERVICE_NAME=your-service-name
export OTEL_SERVICE_VERSION=1.0.0

export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=none

# Pick US or EU based on your account region
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-receiver.eu.sematext.com
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

# Per-signal tokens — only set the ones for Apps you've actually created
export OTEL_EXPORTER_OTLP_TRACES_HEADERS=X-API-TOKEN=<tracing-app-token>
export OTEL_EXPORTER_OTLP_METRICS_HEADERS=X-API-TOKEN=<monitoring-app-token>

See Managed OTLP Endpoint for the full endpoint matrix (US/EU × HTTP/gRPC), compression options, and troubleshooting.

Auto vs manual instrumentation

Style Traces Metrics Logs Code changes
Auto-instrumentation None — just env vars + the extension
Manual instrumentation Explicit SDK init + span creation

Auto-instrumentation is the recommended starting point for Laravel, Symfony, and other supported frameworks. Switch to (or add) manual instrumentation when you need:

  • Custom spans around your own business logic
  • Custom span attributes (user tier, feature flag, etc.)
  • Log shipping (auto doesn't cover OTel logs in PHP today)

The two styles compose: you can add manual spans on top of auto-instrumentation in the same application.

Manual instrumentation example

Bootstrap the SDK and create a custom span:

<?php
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\StatusCode;

require __DIR__ . '/vendor/autoload.php';

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

$span = $tracer->spanBuilder('checkout')->startSpan();
$scope = $span->activate();
try {
    $span->setAttribute('user.tier', $userTier);
    // ... your code ...
    $span->setStatus(StatusCode::STATUS_OK);
} catch (\Throwable $e) {
    $span->recordException($e);
    $span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
    throw $e;
} finally {
    $scope->detach();
    $span->end();
}

The SDK reads its exporter config from the same OTEL_EXPORTER_OTLP_* env vars as auto-instrumentation, so the same Flow A / Flow B blocks above apply.

Running a working example

The sematext-otel-onboarding repository has a complete Laravel example with both auto and manual instrumentation, for baremetal / Docker / Kubernetes:

Clone the repo and follow the per-example README — they're self-contained, ready to run against a Sematext account, and a good baseline if your project gets stuck.

Troubleshooting

Symptom Likely cause
php -m | grep opentelemetry returns nothing Extension installed but not enabled in php.ini — confirm with php --ini to see which file is loaded
Auto-instrumentation isn't producing any spans OTEL_PHP_AUTOLOAD_ENABLED=true is missing, or the framework-specific package (opentelemetry-auto-laravel, etc.) isn't installed
Traces ship but metrics don't OTEL_METRICS_EXPORTER=otlp not set, or the metrics endpoint isn't reachable
Logs don't ship even with manual instrumentation OTel logs in PHP are newer — confirm your open-telemetry/sdk version and logger configuration
401 / 403 from the managed endpoint Token in OTEL_EXPORTER_OTLP_*_HEADERS is for the wrong region, or sent as Authorization: Bearer instead of X-API-TOKEN

See also