Skip to content

Best Practices for Observability of OpenTelemetry PHP Services


Preface

This article describes how to integrate PHP services into Guance based on GuanceCloud/opentelemetry-php-instrumentation, and supplement logs, PHP-FPM metrics, and Profiling to form a directly deployable observability solution.

This solution is suitable for common deployment modes such as PHP-FPM, CLI Worker, Supervisor, and PHP processes inside containers. Trace data is preferably reported via DataKit 4317 port using OTLP gRPC; during debugging and troubleshooting, you can temporarily switch to HTTP OTLP via 9529/otel to quickly verify whether the trace is working.

Environment Information

  • System environment: Linux
  • Development language: PHP 8.2
  • Deployment mode: PHP-FPM / CLI
  • APM extension: GuanceCloud/opentelemetry-php-instrumentation 1.3.1-gtrace
  • Collector: DataKit

Goals

  • Application trace integration
  • Application log integration
  • PHP-FPM metrics integration
  • Profiling integration (optional)

Integration Plan

Notes on the GuanceCloud Fork

The integration method in this article is based on GuanceCloud/opentelemetry-php-instrumentation, which is a fork of the official OpenTelemetry repository.

Two boundaries must be clearly stated:

  • The extension source can use the release assets from the GuanceCloud fork
  • The composer require packages open-telemetry/sdk, open-telemetry/exporter-otlp, open-telemetry/opentelemetry-auto-* are still the official PHP packages

In other words, the current fork mainly modifies the PHP extension layer, and does not mean that the entire PHP SDK, exporter, and auto-instrumentation ecosystem has been forked.

Component List

To implement OpenTelemetry PHP, at least the following components are required:

  • DataKit: responsible for receiving traces, logs, metrics, and profile data
  • PHP opentelemetry extension: responsible for auto-instrumentation capabilities
  • OpenTelemetry PHP SDK: responsible for data construction and export
  • OTLP Exporter: responsible for reporting to DataKit via gRPC or HTTP
  • Instrumentation library for the corresponding framework or component: responsible for automatic instrumentation of Laravel, Slim, Symfony, PDO, Guzzle, etc.

Among these, installing the opentelemetry extension alone is not enough; the project must also install SDK + exporter + corresponding auto-instrumentation packages. Otherwise, even if the extension is loaded successfully, complete trace data cannot be formed.

Prerequisites

Install DataKit

The host must first install DataKit and ensure that 127.0.0.1:4317 or 127.0.0.1:9529 is accessible from the host.

Enable the OpenTelemetry Collector

Go to the DataKit installation directory, copy the sample configuration, and restart DataKit:

mkdir -p /usr/local/datakit/conf.d/opentelemetry
cp /usr/local/datakit/conf.d/samples/opentelemetry.conf.sample \
  /usr/local/datakit/conf.d/opentelemetry/opentelemetry.conf
systemctl restart datakit

Check whether DataKit is running normally:

curl http://127.0.0.1:9529/v1/ping
ss -lntp | grep -E '4317|9529'
Warning

If the application sends data to http://127.0.0.1:9529/otel/v1/traces and receives a 404 response, the problem is usually not on the PHP side but that the opentelemetry input of DataKit is not actually enabled. First, check whether opentelemetry.conf exists and has taken effect after DataKit restarted.

Trace Integration

1. Install and Enable the OTEL Extension

According to the official OpenTelemetry zero-code documentation, the shortest path should prioritize "system package" or "image installation" instead of compilation from the start.

The recommended order is as follows:

  1. Linux package manager installation
  2. Docker image installation
  3. Use pecl only if the host already has it
  4. Source code compilation as a last resort
Warning

Do not assume that the Linux host will always have pecl. For example, the host used for verification in this article, as of 2026-07-30, actually had phpize but not pecl.

Also note that yum install php-pecl-opentelemetry or pecl install opentelemetry usually installs the official upstream extension, not the gtrace version from the GuanceCloud fork. If your goal is to verify or deliver 1.3.1-gtrace, the installation source must be changed to the release assets of the GuanceCloud fork or your internal build artifacts.

If your distribution has a ready-made package, install it directly.

For CentOS/RHEL, you can use the official Remi repository method:

yum update -y
yum install -y epel-release yum-utils
yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum-config-manager --enable remi-php81
yum install -y php php-pecl-opentelemetry
php --ri opentelemetry

For Alpine, you can directly install the APK package:

echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk add php php81-pecl-opentelemetry@testing
php --ri opentelemetry

For official Docker PHP images:

install-php-extensions opentelemetry

If the host already has pecl, you can directly execute:

pecl install opentelemetry

If the host does not have pecl, choose one of the following methods:

  • Provide the opentelemetry.so matching the PHP version directly from the platform side
  • Install using the system package manager
  • Install via install-php-extensions inside the container image

For the GuanceCloud fork, the currently recommended enterprise deployment methods are:

  • Windows: Use the pre-packaged zip assets in the release directly
  • Linux: Build the opentelemetry.so for the corresponding PHP minor version via CI, then distribute it uniformly through operations or an artifact repository

This ensures that the installed version is indeed the gtrace version, not the official upstream extension.

For Windows:

  • Download the zip package matching the current PHP minor version, ts/nts, and compiler from the GuanceCloud/opentelemetry-php-instrumentation Release page
  • Place php_opentelemetry.dll into the PHP ext/ directory
  • Enable the extension in php.ini

Regardless of the method used, when selecting the extension package, you must strictly match the following conditions:

  • PHP minor version, e.g., 8.1/8.2/8.3/8.4
  • ts/nts
  • Platform and compiler
  • The PHP binary corresponding to the target runtime mode

Enable the extension in php.ini:

[opentelemetry]
extension=opentelemetry.so
opentelemetry.attr_hooks_enabled = On

Confirm that the extension is loaded:

php --ri opentelemetry

2. Install SDK and Auto-Instrumentation Dependencies

The key point from the official documentation is: installing the extension alone does not generate traces. The project must also install SDK + exporter + instrumentation libraries.

Using Slim + PSR-18 as an example, the minimum dependencies are as follows:

composer config allow-plugins.php-http/discovery false
composer require \
  open-telemetry/sdk \
  open-telemetry/exporter-otlp \
  php-http/guzzle7-adapter \
  nyholm/psr7

If your application is not Slim but another framework or component, install the corresponding auto-instrumentation package according to your actual technology stack. Common examples are as follows:

# PSR-18 HTTP Client
composer require open-telemetry/opentelemetry-auto-psr18

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

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

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

# PDO
composer require open-telemetry/opentelemetry-auto-pdo

# Guzzle
composer require open-telemetry/opentelemetry-auto-guzzle

The minimum principle is:

  • Have the extension
  • Have sdk
  • Have exporter-otlp
  • Have auto-* packages matching your current framework and middleware

Otherwise, you will only get incomplete or even empty trace data.

3. Configure Trace Reporting Parameters

It is recommended to use OTLP gRPC to directly connect to the local DataKit on 4317:

OTEL_PHP_AUTOLOAD_ENABLED=true \
OTEL_SERVICE_NAME=my-php-service \
OTEL_SERVICE_VERSION=1.3.1-gtrace \
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod \
OTEL_TRACES_EXPORTER=otlp \
OTEL_METRICS_EXPORTER=none \
OTEL_LOGS_EXPORTER=none \
OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317 \
OTEL_PROPAGATORS=baggage,tracecontext

When troubleshooting, you can switch to HTTP OTLP:

OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:9529/otel

If you use PHP-FPM, Apache, Supervisor, systemd, or containers to start PHP, configure the above environment variables into the process startup environment, not just in the current shell session.

The recommended minimum configuration is as follows:

OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=my-php-service
OTEL_SERVICE_VERSION=1.3.1-gtrace
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=none
OTEL_LOGS_EXPORTER=none
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317

To distinguish environments, add:

OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod

4. Add Business Instrumentation

Framework auto-instrumentation can only cover general entry points. For key business nodes such as order placement, payment, external API calls, and batch processing, it is recommended to use WithSpan to add instrumentation:

<?php

declare(strict_types=1);

use OpenTelemetry\API\Instrumentation\SpanAttribute;
use OpenTelemetry\API\Instrumentation\WithSpan;

#[WithSpan('order.submit')]
function submitOrder(#[SpanAttribute] string $orderNo): void
{
    // Business logic
}

Log Integration

It is recommended that the application output JSON logs and explicitly write service, env, and version. If you need to correlate logs with traces, output the current trace_id and span_id together:

use OpenTelemetry\API\Trace\Span;

$context = Span::getCurrent()->getContext();
$traceId = $context->isValid() ? $context->getTraceId() : '';
$spanId = $context->isValid() ? $context->getSpanId() : '';

Enable the logging collector on the DataKit side. Example configuration:

[[inputs.logging]]
  logfiles = ["/var/log/php-app/*.log"]
  source = "php"
  service = "my-php-service"
  pipeline = "php-json.p"

It is recommended that the log fields include at least:

  • message
  • status
  • service
  • env
  • version
  • trace_id
  • span_id

PHP-FPM Metrics Integration

If the application runs via PHP-FPM, it is recommended to also enable the phpfpm collector.

  1. Enable the status page in www.conf:
pm.status_path = /status
  1. Expose /status through Nginx or Apache.

  2. Enable the phpfpm collector on DataKit:

[[inputs.phpfpm]]
  status_url = "http://127.0.0.1/status"
  use_fastcgi = false

This allows you to monitor:

  • Active processes
  • Idle processes
  • Connection queue length
  • Number of slow requests
  • Request time and memory consumption per process

Profiling Integration (Optional)

If you need to locate CPU, memory allocation, and hotspot functions in addition to traces, you can enable PHP Profiling separately.

The current more reliable approach is:

  1. Enable the profile collector on DataKit
  2. Install dd-trace-php on PHP and enable profiling
  3. Configure the following environment variables for the PHP process:
DD_PROFILING_ENABLED=true
DD_SERVICE=my-php-service
DD_ENV=prod
DD_VERSION=1.3.1-gtrace
DD_AGENT_HOST=127.0.0.1
DD_TRACE_AGENT_PORT=9529

Profiling is a supplementary capability to traces. It is recommended to enable it on demand, not by default on all services.

Minimum Integration Sequence

If your goal is to first get traces working, then gradually supplement other observability data, it is recommended to follow this order:

  1. Enable the opentelemetry input on DataKit
  2. Install and enable the PHP opentelemetry extension
  3. Install the sdk + exporter-otlp + corresponding auto-* dependencies in the project
  4. Configure the OTEL_* process environment variables
  5. Make a real request and confirm that the trace has entered Guance
  6. Then add log collection, PHP-FPM metrics, and Profiling

This shortens the trace debugging steps to a minimum, avoiding handling too many components at once.

Current Limitations of the Fork

If fully aligning with the official OpenTelemetry zero-code experience, the current GuanceCloud fork still has several obvious gaps:

  • No pre-built Linux binaries are available, so the installation path on Linux is not yet short enough
  • No independent, identifiable package distribution channel; pecl install opentelemetry will land on the upstream, not gtrace
  • The release assets are not fully integrated with the "system package installation" path in the official documentation, so the documentation cannot directly copy the official installation commands
  • It is necessary to separately explain that "the extension comes from the fork, but the SDK and auto-instrumentation dependencies still come from the official Composer packages"

If the integration experience is to be further improved in the future, the priority is recommended as follows:

  1. Add pre-compiled extension assets for common PHP minor versions for Linux
  2. Provide an installation script that automatically detects the PHP version, ts/nts, and extension directory
  3. On the release page, clearly indicate "which PHP version/runtime mode this asset is suitable for"
  4. In the fork README, clearly distinguish between the "upstream installation path" and the "GuanceCloud gtrace installation path"

Practical Effects

After completing the above integration, you can generally obtain the following observability capabilities:

  • View trace information such as PHP service entry requests, database calls, downstream HTTP calls, etc., in APM
  • Filter logs by service, env, version, and trace back to the same request based on trace_id
  • Observe the running status of PHP-FPM, such as active processes, queuing, and slow requests, in Infrastructure or custom views
  • Locate CPU hotspots, memory allocation hotspots, and slow functions in the Profiling view

Best Practice Recommendations

  • Unify resource attributes: at least standardize service.name, service.version, and deployment.environment
  • Unify release naming: keep the extension version, application version, and release label consistent to facilitate version tracing
  • Prefer 4317/gRPC: prioritize OTLP gRPC in production, and use 9529/otel for debugging and troubleshooting
  • Prefer ready-made installation methods: prioritize image pre-installation, pre-built artifact distribution, or PECL installation when pecl is available; do not recommend source code compilation as the primary path
  • Use process-level environment variables: do not set OTEL_* only in the login shell; write them into php-fpm, systemd, containers, or startup scripts
  • Control attribute cardinality: do not write full sets of phone numbers, order numbers, raw SQL, or excessively long URLs into span attributes
  • Add manual instrumentation for key business logic: use WithSpan for business nodes not covered by auto-instrumentation
  • Keep trace fields in logs: uniformly output trace_id and span_id, otherwise logs and traces cannot be reliably correlated
  • Check DataKit inputs first: when traces are not working, first confirm whether the opentelemetry, logging, phpfpm, and profile collectors are enabled

References

Feedback

Is this page helpful?