Monitoring PHP applications with OpenTelemetry and SigNoz

ankit01oss

Ankit Anand ✨

Posted on June 13, 2022

Monitoring PHP applications with OpenTelemetry and SigNoz

PHP is a widely popular server-side language and enjoys the top spot in terms of market share. Many world-famous organizations like Facebook have their applications written in PHP. WordPress, which powers 43% of all websites, is also built on PHP. In this tutorial, we will use OpenTelemetry to instrument a PHP application for telemetry data.

It’s essential to monitor your PHP application for performance issues and bugs. Application owners need good telemetry data from their application in order to monitor it effectively. That’s where OpenTelemetry comes into the picture. OpenTelmetry provides client libraries for many programming languages, including PHP, which can be used to instrument applications.

What is application instrumentation?

Instrumentation is the process of enabling your application code to generate telemetry data(logs, metrics, and traces). OpenTelemetry provides both auto instrumentation libraries and APIs to manually instrument your application.

OpenTelemetry helps in generating and collecting the telemetry data. The collected data then needs to be sent to a backend analysis tool. OpenTelemetry provides you the freedom to select any backend tool that can help you store and visualize the telemetry data. And that’s where SigNoz comes into the picture.

SigNoz & OpenTelemetry

SigNoz.io is a full-stack open-source application monitoring and observability platform
 which can be installed within your infra. It provides metrics monitoring, distributed tracing, exceptions monitoring, and custom dashboards - everything under a single pane of glass. You can also set alerts on your critical metrics to keep yourself notified.

SigNoz is built to natively support OpenTelemerty, thus making it a great choice for the OpenTelemetry backend.

Installing SigNoz

SigNoz can be installed on macOS or Linux computers in just three steps by using a simple installation script.

The install script automatically installs Docker Engine on Linux. However, on macOS, you must manually install Docker Engine before running the install script.

git clone -b main https://github.com/SigNoz/signoz.git
cd signoz/deploy/
./install.sh
Enter fullscreen mode Exit fullscreen mode

You can visit our documentation for instructions on how to install SigNoz using Docker Swarm and Helm Charts.

Deployment Docs

When you are done installing SigNoz, you can access the UI at http://localhost:3301

SigNoz dashboard
SigNoz dashboard - It shows services from a sample app that comes bundled with the application

Instrument your PHP app with OpenTelemetry

Step 1: Install the required dependencies from OTel PHP library:

use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\API\Trace\SpanKind;
Enter fullscreen mode Exit fullscreen mode

Step 2: Initialise the tracer module and create tracer:

$tracerProvider =  new TracerProvider(
    new SimpleSpanProcessor(
        new ConsoleSpanExporter()
    )
);

$tracer = $tracerProvider->getTracer('io.opentelemetry.contrib.php',);
Enter fullscreen mode Exit fullscreen mode

Step 3: Creating spans

Create root span and activate it:

$rootSpan = $tracer->spanBuilder('root')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
$rootSpan->activate();
Enter fullscreen mode Exit fullscreen mode

Create and initiate your first span:

$span1 = $tracer->spanBuilder('foo')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
$span1->activate();
Enter fullscreen mode Exit fullscreen mode

Create another span:

$span2 = $tracer->spanBuilder('bar')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
Enter fullscreen mode Exit fullscreen mode

Make sure to end them:

$span2->end();
$span1->end();
Enter fullscreen mode Exit fullscreen mode

Now end the root span:

$rootSpan->end();
Enter fullscreen mode Exit fullscreen mode

Here’s how it looks when everything is assembled with a bit of error handling:

<?php

declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';

use OpenTelemetry\SDK\Trace\SpanExporter\ConsoleSpanExporter;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\API\Trace\SpanKind;

echo 'Starting ConsoleSpanExporter' . PHP_EOL;

$tracerProvider =  new TracerProvider(
    new SimpleSpanProcessor(
        new ConsoleSpanExporter()
    )
);

$tracer = $tracerProvider->getTracer('io.opentelemetry.contrib.php',);

$rootSpan = $tracer->spanBuilder('root')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
$rootSpan->activate();

try {
    $span1 = $tracer->spanBuilder('foo')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
    $span1->activate();

    try {
        $span2 = $tracer->spanBuilder('bar')->setSpanKind(SpanKind::KIND_SERVER)->startSpan();
        echo 'OpenTelemetry welcomes PHP' . PHP_EOL;
    } finally {
        $span2->end();
    }
} finally {
    $span1->end();
}
$rootSpan->end();
Enter fullscreen mode Exit fullscreen mode

Step 4: Running the PHP application

Run your PHP application with the following command:

php 1-getting-started-console-exporter.php
Enter fullscreen mode Exit fullscreen mode

You’ll see an output like this:

//Output
Starting ConsoleSpanExporter
OpenTelemetry welcomes PHP
[
    {
        "name": "bar",
        "context": {
            "trace_id": "8e447a8a939de0a65c3d2c5255012398",
            "span_id": "b829010efdadb302",
            "trace_state": ""
        },
        "resource": {
            "host.name": "Pranshus-MacBook-Pro.local",
            "host.arch": "arm64",
            "os.type": "darwin",
            "os.description": "21.4.0",
            "os.name": "Darwin",
            "os.version": "Darwin Kernel Version 21.4.0: Fri Mar 18 00:47:26 PDT 2022; root:xnu-8020.101.4~15\/RELEASE_ARM64_T8101",
            "process.pid": 42913,
            "process.executable.path": "\/opt\/homebrew\/Cellar\/php\/8.1.5\/bin\/php",
            "process.command": ".\/src\/1-getting-started-console-exporter.php",
            "process.command_args": [
                ".\/src\/1-getting-started-console-exporter.php"
            ],
            "process.owner": "pranshuchittora",
            "process.runtime.name": "cli",
            "process.runtime.version": "8.1.5",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "dev-main",
            "service.name": "unknown_service"
        },
        "parent_span_id": "c6b0f081c30f5519",
        "kind": "KIND_SERVER",
        "start": 1652959406327378349,
        "end": 1652959406327383891,
        "attributes": [],
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": []
    }
]
[
    {
        "name": "foo",
        "context": {
            "trace_id": "8e447a8a939de0a65c3d2c5255012398",
            "span_id": "c6b0f081c30f5519",
            "trace_state": ""
        },
        "resource": {
            "host.name": "Pranshus-MacBook-Pro.local",
            "host.arch": "arm64",
            "os.type": "darwin",
            "os.description": "21.4.0",
            "os.name": "Darwin",
            "os.version": "Darwin Kernel Version 21.4.0: Fri Mar 18 00:47:26 PDT 2022; root:xnu-8020.101.4~15\/RELEASE_ARM64_T8101",
            "process.pid": 42913,
            "process.executable.path": "\/opt\/homebrew\/Cellar\/php\/8.1.5\/bin\/php",
            "process.command": ".\/src\/1-getting-started-console-exporter.php",
            "process.command_args": [
                ".\/src\/1-getting-started-console-exporter.php"
            ],
            "process.owner": "pranshuchittora",
            "process.runtime.name": "cli",
            "process.runtime.version": "8.1.5",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "dev-main",
            "service.name": "unknown_service"
        },
        "parent_span_id": "b641f1ef2683f70f",
        "kind": "KIND_SERVER",
        "start": 1652959406327364474,
        "end": 1652959406327551099,
        "attributes": [],
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": []
    }
]
[
    {
        "name": "root",
        "context": {
            "trace_id": "8e447a8a939de0a65c3d2c5255012398",
            "span_id": "b641f1ef2683f70f",
            "trace_state": ""
        },
        "resource": {
            "host.name": "Pranshus-MacBook-Pro.local",
            "host.arch": "arm64",
            "os.type": "darwin",
            "os.description": "21.4.0",
            "os.name": "Darwin",
            "os.version": "Darwin Kernel Version 21.4.0: Fri Mar 18 00:47:26 PDT 2022; root:xnu-8020.101.4~15\/RELEASE_ARM64_T8101",
            "process.pid": 42913,
            "process.executable.path": "\/opt\/homebrew\/Cellar\/php\/8.1.5\/bin\/php",
            "process.command": ".\/src\/1-getting-started-console-exporter.php",
            "process.command_args": [
                ".\/src\/1-getting-started-console-exporter.php"
            ],
            "process.owner": "pranshuchittora",
            "process.runtime.name": "cli",
            "process.runtime.version": "8.1.5",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "dev-main",
            "service.name": "unknown_service"
        },
        "parent_span_id": "",
        "kind": "KIND_SERVER",
        "start": 1652959406327223724,
        "end": 1652959406327563766,
        "attributes": [],
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": []
    }
]
Enter fullscreen mode Exit fullscreen mode

Monitor your PHP application with SigNoz

For this, we will be generating spans using a for loop. Also, we will be attaching attributes in each span which can help us collect important metadata that proves helpful during debugging.

Import OTel and Guzzle (for HTTP) dependencies:

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use OpenTelemetry\Contrib\OtlpHttp\Exporter as OTLPExporter;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
Enter fullscreen mode Exit fullscreen mode

We need to define the environment variable for OTLP endpoint. SigNoz uses port 4318 to listen to data collected by OpenTelemetry from PHP applications. Since we have installed SigNoz on localhost, the OTLP endpoint is:

OTEL_EXPORTER_OTLP_ENDPOINT - http://localhost:4318/v1/traces

Define the environment variables:

putenv('OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces'); // SigNoz OTel collector's path
Enter fullscreen mode Exit fullscreen mode

Optionally you can pass the env variable while running the app.

Initialize the exporter:

$exporter = new OTLPExporter(
    new Client(),
    new HttpFactory(),
    new HttpFactory()
);
Enter fullscreen mode Exit fullscreen mode

Initialize the Tracer Provider with the exporter:

$tracerProvider =  new TracerProvider(
    new SimpleSpanProcessor(
        $exporter
    )
);
Enter fullscreen mode Exit fullscreen mode

Create and activate your root span:

$root = $span = $tracer->spanBuilder('root')->startSpan();
$span->activate();
Enter fullscreen mode Exit fullscreen mode

Create, initialize, set data, and end spans inside the for loop:

for ($i = 0; $i < 3; $i++) {
    // start a span, register some events
    $span = $tracer->spanBuilder('loop-' . $i)->startSpan();

    $span->setAttribute('remote_ip', '1.2.3.4')
        ->setAttribute('country', 'USA');

    $span->addEvent('found_login' . $i, new Attributes([
        'id' => $i,
        'username' => 'otuser' . $i,
    ]));
    $span->addEvent('generated_session', new Attributes([
        'id' => md5((string) microtime(true)),
    ]));

    $span->end();
}
Enter fullscreen mode Exit fullscreen mode

Don’t forget to end the root span:

$root->end();
Enter fullscreen mode Exit fullscreen mode

Run your PHP application:

> OTEL_SERVICE_NAME=signoz-php-app php ./src/2-send-trace-to-collector.php
Enter fullscreen mode Exit fullscreen mode

Once you run your application, you can interact with it a bit to generate some dummy monitoring data.

Now open SigNoz UI at: http://localhost:3301, and go to the Traces tab, and select the span from service signoz-php-app.

SigNoz dashboard
Traces tab of SigNoz dashboard comes with a powerful set of filters to analyze your tracing data

After selecting the span you will land on the Trace Detail Page where you can visualize the entire request with the help of Flamegraphs and Gantt charts. OpenTelemetry captures every component of a software system with the help of attributes(key-value pairs). You can also see these attributes for every span which will help you build more context.

SigNoz dashboard
Flamegraphs and Gantt charts shows the breakdown of a request. The attributes panel gives you more contextual information on each span.

Conclusion

Using OpenTelemetry libraries, you can instrument your PHP applications for setting up observability. You can then use an open-source APM tool like SigNoz to ensure the smooth performance of your PHP applications.

OpenTelemetry is the future for setting up observability for cloud-native apps. It is backed by a huge community and covers a wide variety of technology and frameworks. Using OpenTelemetry, engineering teams can instrument polyglot and distributed applications with peace of mind.

SigNoz is an open-source observability tool that comes with a SaaS-like experience. You can try out SigNoz by visiting its GitHub repo 👇

SigNoz GitHub repo

If you have any questions or need any help in setting things up, join our slack community and ping us in #support channel.

SigNoz Slack community


Further Reading

SigNoz - an open-source alternative to DataDog

Monitor your Spring Boot application with OpenTelemetry

💖 💪 🙅 🚩
ankit01oss
Ankit Anand ✨

Posted on June 13, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related