PHP является самым популярным языком для серверной разработки, по праву занимая первое место на рынке. Приложения многих всемирно известных организаций, таких как Facebook, написаны на PHP. WordPress, на котором работает 43% всех веб-сайтов, также создан на основе PHP. В этом туториале я научу вас инструментировать PHP-приложение при помощи OpenTelemetry для получения данных телеметрии.

Мониторинг PHP-приложения на предмет проблем с производительностью и ошибок очень важен. Чтобы эффективно мониторить приложение, вам нужны надежные данные телеметрии из него. И с этим нам может помочь OpenTelemetry. OpenTelmetry предоставляет клиентские библиотеки для множества языков программирования, включая PHP, которые можно использовать для инструментирования приложений.

Что такое инструментирование приложения?

Инструментирование — это внедрение в код приложения процессов генерации данных телеметрии (логов, метрик и трейсов). OpenTelemetry предоставляет как библиотеки автоматического инструментирования, так и API для ручного инструментирования приложения.

OpenTelemetry помогает генерировать и собирать данные телеметрии. Затем собранные данные необходимо отправить в инструмент анализа на бэкенде. OpenTelemetry предоставляет свободу выбора любого внутреннего инструмента, с помощью которого будет удобнее всего хранить и визуализировать данные телеметрии. А с этим нам может помочь SigNoz.

SigNoz и OpenTelemetry

SigNoz.io — это опенсорсная платформа для комплексного мониторинга и наблюдения за приложениями, которую можно развернуть в вашей инфраструктуре. SigNoz обеспечивает мониторинг метрик и исключений, распределенную трассировку и настраиваемые дашборды — все это в пределах одного окна. Вы также можете настроить оповещения с важными метриками, чтобы всегда быть в курсе.

SigNoz нативно поддерживает OpenTelemerty, что делает его отличным выбором для бэкенда OpenTelemetry.

Установка SigNoz

С помощью простого скрипта установки SigNoz можно установить на компьютеры с macOS или Linux всего за три шага.

В Linux скрипт установки автоматически устанавливает Docker Engine. Однако в macOS нужно вручную установить Docker Engine перед запуском скрипта установки.

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

В нашей документации вы можете найти детальные инструкции по установке SigNoz с помощью Docker Swarm и Helm Charts.

Когда вы закончите с установкой SigNoz, доступ к пользовательскому интерфейсу можно будет получить по адресу http://localhost:3301

Дашборд SigNoz — здесь он отображает сервисы из примера, который поставляется в комплекте с приложением

Инструментирование PHP-приложения при помощи OpenTelemetry:

Шаг 1: Установка необходимых зависимостей из PHP-библиотеки OpenTelemetry:

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

Шаг 2: Инициализация модуля трассировщика и создание самого трассировщика:

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

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

Шаг 3: Создание спанов (диапазонов)

Создайте корневой спан и активируйте его:

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

Создайте и инициируйте свой первый спан:

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

Создайте еще один спан:

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

Не забудьте закрыть их:

$span2->end();
$span1->end();

Теперь закройте корневой спан:

$rootSpan->end();

Вот как это все выглядит, когда собрано воедино + немного обработки ошибок:

<?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();

Шаг 4: Запуск PHP-приложения 

Запустите PHP-приложение при помощи следующей команды:

php 1-getting-started-console-exporter.php

Вы должны увидеть подобный вывод:

// Вывод
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": []
    }
]

Мониторинг PHP-приложение с помощью SigNoz

 Для этого мы будем генерировать спаны в цикле for. Кроме того, мы будем присоединять к каждому сапны атрибуты, которые могут помочь нам собрать важные метаданные, которые будут полезными для нас во время отладки.

 Импортируйте зависимости OpenTelemetry и Guzzle (для HTTP):

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;

Нам нужно определить переменную среды для конечной точки OTLP. Для прослушивания данных, собранных OpenTelemetry из PHP-приложений, SigNoz использует порт 4318. Поскольку мы установили SigNoz на локальном хосте, конечная точка OTLP будет:

 

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

Определите переменные среды:

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

При желании вы можете передавать переменную env во время работы приложения.

 Инициализируйте экспортер:

$exporter = new OTLPExporter(
    new Client(),
    new HttpFactory(),
    new HttpFactory()
);

Инициализируйте Tracer Provider с экспортером:

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

Создайте и активируйте корневой спан:

$root = $span = $tracer->spanBuilder('root')->startSpan();
$span->activate();

Создание, инициализация, установка данных и закрытие спанов внутри цикла for:

for ($i = 0; $i < 3; $i++) {
    // открытие спана, регистрация некоторых событий
    $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();
}

Не забудьте закрыть корневой спан:

$root->end();

Запустите ваше PHP-приложение:

> OTEL_SERVICE_NAME=signoz-php-app php ./src/2-send-trace-to-collector.php

После запуска приложения вы можете немного поработать с ним, чтобы сгенерировать фиктивные данные для мониторинга.

Теперь откройте пользовательский интерфейс SigNoz по адресу: http://localhost:3301, перейдите на вкладку Traces и выберите спан из сервиса signoz-php-app.

Вкладка Traces дашборда SigNoz содержит мощный набор фильтров для анализа данных трассировки

После выбора спана вы попадете на Trace Detail Page (страницу сведений о трейсе), где сможете визуализировать запрос с помощью Flamegraph’ов и диаграмм Ганта. OpenTelemetry фиксирует каждый компонент программной системы посредством атрибутов (пар ключ-значение). Вы также можете просмотреть все эти атрибуты для каждого спана, что поможет вам проще вникнуть в контекст.

Flamegraph’ы и диаграммы Ганта показывают нам детализацию запроса. Панель атрибутов предоставляет нам наглядную контекстную информацию о каждом спане.

Заключение

Используя библиотеки OpenTelemetry, вы можете значительно повысить наблюдаемость своего PHP-приложения. Затем вы можете использовать какой-нибудь опенсорсный APM-инструмент, такой как SigNoz, чтобы обеспечить бесперебойную работу ваших PHP-приложений.

OpenTelemetry — это будущее в мире наблюдаемости облачных приложений. Он поддерживается огромным сообществом и охватывает широкий спектр технологий и фреймворков. Используя OpenTelemetry, команды разработчиков могут спокойно управлять многоязычными и распределенными приложениями.

SigNoz — это опенсорсный инструмент для мониторинга приложений, который работает как SaaS. Вы можете попробовать SigNoz, посетив его GitHub-репозиторий

Если у вас есть какие-либо вопросы или вам нужна помощь в настройке, присоединяйтесь к нашему slack-сообществу и пишите в #support.

Дополнительная литература:

SigNoz — альтернатива DataDog с открытым исходным кодом.

Мониторинг вашего Spring Boot приложения с помощью OpenTelemetry


Приглашаем всех желающих на бесплатный мастер-класс по разработке одностраничного приложения на PHP с помощью Symfony и Vue.js, на котором мы:
- разработаем API на стороне back-end,
- создадим несложное приложение для работы с этим API на стороне front-end и
- настроим JWT-аутентификацию.
Регистрация по ссылке.