Integrate OpenTelemetry into a PHP Application bu using Manual Instrumentation

Ankita LunawatAnkita Lunawat
6 min read

Integrate OpenTelemetry for PHP Application with manual instrumentation to enable detailed observability by collecting, processing, and exporting telemetry data like traces and logs, allowing direct control over which parts of the application are monitored.

Prerequisites

AWS Account with Ubuntu 24.04 LTS EC2 Instance.

PHP, PECL, composer installed.

update the packages.

sudo apt update

Installs the command-line interface for PHP 8.3.

sudo apt install php8.3-cli

Verify the installation.

php -v

Install PECL to get the necessary tools for developing PHP extensions.

sudo apt install php-pear php8.3-dev

Download and install Composer, a tool that helps manage PHP libraries more easily.

curl -sS https://getcomposer.org/installer | php

Move the Composer binary to a directory in /usr/local/bin/ to make it accessible globally.

sudo mv composer.phar /usr/local/bin/composer

Run composer -V to check the Composer version and verify the installation.

composer -v

Install Build Tools needed for creating PECL extensions.

sudo apt-get install gcc make autoconf

Create your PHP project with the Slim Framework.

Create a new project directory.

mkdir opentelemetry-php-example

Move to the directory.

cd opentelemetry-php-example

Initialize a new Composer project and add Slim as a dependency.

composer init --no-interaction --require slim/slim:"^4" --require slim/psr7:"^1"

Install the dependencies defined in composer.json using the following command.

composer update

Create an index.php file that contains a simple Slim application.

nano index.php

add the following code into it.

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) {
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    return $response;
});

$app->run();

Explanation of the code

This code begins by importing necessary classes and loading dependencies installed with Composer. It then initializes a new Slim application instance, with the $app variable serving as the application object to manage routes and handle HTTP requests. A route (/rolldice) is defined, which generates a random integer between 1 and 6, writes it to the response, and sends it back to the client. Finally, the application is started to listen for incoming requests.

Now, start the built-in PHP server to test your application with the following command.

php -S 0.0.0.0:8080

Open http://<Public-IP-Address>:8080/rolldice in your browser. You should see a random number between 1 and 6.

Integrate OpenTelemetry for PHP Application with Manual Instrumentation to get Trace data

First install the OpenTelemetry SDK.

composer require open-telemetry/sdk

Modify index.php to add OpenTelemetry manual tracing.

nano index.php

add the following code into it.

<?php

use OpenTelemetry\API\Globals;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

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

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

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) use ($tracer) {
    $span = $tracer
        ->spanBuilder('manual-span')
        ->startSpan();
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    $span
        ->addEvent('rolled dice', ['result' => $result])
        ->end();
    return $response;
});

$app->run();

Explanation of the code:

The code starts with Imports and Autoloading, importing necessary classes from OpenTelemetry, Slim, and PSR-7, using the Globals class for OpenTelemetry's global tracer, ResponseInterface and ServerRequestInterface for HTTP request and response structures, and AppFactory to create a Slim application instance, while the Autoloading part uses the require statement to include Composer’s autoload file for loading dependencies from composer.json.

Create Application: This initializes a new Slim application instance using AppFactory. The $app variable now holds the application object that will manage routes and handle HTTP requests.

Define the /rolldice Route: Defines a route (/rolldice) that listens for GET requests. Manual Tracing with OpenTelemetry: spanBuilder creates a new span (manual-span) to capture trace data for this request. startSpan() begins the span, recording this operation in the trace.

Rolls a Dice: random_int(1,6) generates a random number from 1 to 6, simulating a dice roll. Returns the Result: The result is written to the response body using write(), and addEvent() logs the event within the trace, capturing the result as additional data. end() closes the span, marking the end of this traced operation. The final response is sent to the client with the dice roll result.

Run the Application: Starts the PHP application and begins listening for incoming requests. Slim routes them to their corresponding handlers, which process the request and return a response.

Run the PHP application with OpenTelemetry environment variables to display tracing output in the console.

env OTEL_PHP_AUTOLOAD_ENABLED=true \
    OTEL_TRACES_EXPORTER=console \
    OTEL_METRICS_EXPORTER=none \
    OTEL_LOGS_EXPORTER=none \
    php -S 0.0.0.0:8080

http://<Public-IP-Address>:8080/rolldice to view trace information in the console, where the OpenTelemetry SDK captures trace details for the /rolldice route, displaying span and event data.

Integrate OpenTelemetry logging with Monolog

Install Monolog and OpenTelemetry logger dependencies.

composer require monolog \ monolog open-telemetry \ opentelemetry-logger-monolog

Modify index.php to add logging functionality using Monolog with OpenTelemetry

nano index.php

Add the following code into it.

<?php

use Monolog\Logger;
use OpenTelemetry\API\Globals;
use OpenTelemetry\Contrib\Logs\Monolog\Handler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;

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

$loggerProvider = Globals::loggerProvider();
$handler = new Handler(
    $loggerProvider,
    LogLevel::INFO
);
$monolog = new Logger('otel-php-monolog', [$handler]);

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) use ($monolog) {
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    $monolog->info('dice rolled', ['result' => $result]);
    return $response;
});

$app->run();

Explanation of the code

  1. Imports and Autoloading:

    • Imports necessary classes for logging, OpenTelemetry, HTTP messaging, and Slim:

      • Logger: Monolog’s main class for logging events.

      • Globals and Handler: Access to OpenTelemetry’s global logger provider and a handler to integrate OpenTelemetry with Monolog.

      • ResponseInterface and ServerRequestInterface: Define HTTP request and response structures for handling them in Slim.

      • LogLevel: Provides log severity levels, such as INFO.

      • AppFactory: Used to create the Slim application instance.

    • Autoloading: Loads dependencies specified in composer.json through Composer’s autoloader.

  2. Set Up Logging:

    • Initialize OpenTelemetry Logger Provider: Retrieves the global logger provider from OpenTelemetry.

    • Create a Handler for Monolog:

      • Handler: Sets up a bridge between OpenTelemetry and Monolog.

      • LogLevel::INFO: Defines the minimum log level to capture (INFO and above).

    • Set Up Monolog Logger:

      • Logger: A Monolog instance is created, with OpenTelemetry’s handler attached.

      • The logger name otel-php-monolog will label all logs generated by this instance.

  3. Define the /rolldice Route:

    • Defines a route (/rolldice) that listens for GET requests.

    • Simulates a Dice Roll:

      • random_int(1,6): Generates a random number from 1 to 6, simulating a dice roll.

      • Logs the Dice Roll: info() logs the result at INFO level, with a message dice rolled and attaches the dice roll result as a context property.

      • Returns the Response: The dice roll result is written to the response body, which is then returned to the client.

  4. Run the Application:

    • Starts the php application and begins listening for incoming requests. Slim handles routing to the appropriate handler, which processes the request and returns a response with the dice roll result.

Run the PHP application again with environment variables set up for logging.

env OTEL_PHP_AUTOLOAD_ENABLED=true \
    OTEL_TRACES_EXPORTER=console \
    OTEL_METRICS_EXPORTER=none \
    OTEL_LOGS_EXPORTER=console \
    php -S 0.0.0.0:8080

Access http://<Public-IP-Address>:8080/rolldice to see log messages with dice roll results and trace data from OpenTelemetry on the console.

0
Subscribe to my newsletter

Read articles from Ankita Lunawat directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ankita Lunawat
Ankita Lunawat

I am a dedicated and experienced Cloud Engineer with two years in the industry, specializing in designing, implementing, and managing scalable and secure cloud infrastructures. With a strong foundation in AWS, Azure, and GCP, I excel at leveraging cloud services to optimize performance, enhance security, and reduce operational costs. My expertise includes automated deployment pipelines, infrastructure as code (IaC) with tools like Terraform and container orchestration using Kubernetes and Docker. Throughout my career, I've collaborated with cross-functional teams to deliver robust cloud solutions, ensuring high availability and fault tolerance. I'm passionate about staying at the forefront of cloud technology trends and continuously enhancing my skill set to provide innovative solutions that drive business success. Whether it's migrating legacy systems to the cloud or architecting new cloud-native applications, I bring a strategic approach to every project, focusing on efficiency, scalability, and reliability. In addition to my technical skills, I am an advocate for DevOps practices, promoting a culture of collaboration and continuous improvement within development and operations teams. My commitment to learning and adapting to new technologies ensures that I can meet the evolving needs of any organization and deliver top-tier cloud solutions.